2010-10-04 10 views
3

J'ai un programme qui trouve tous les fichiers de rapports .csv dans un répertoire. Ces rapports sont stockés dans une listeC# liste personnalisée tri

List<string> _items = new List<string>(); 

ce rapport sont nommés comme ceci:

"mois year.csv" (exemple: "Avgust 2010.csv")

Je voudrais trier mes rapports par mois.

mois sont dans cet ordre:

Januar Februar, Marec, avril, maj, junij, julij, avgust, septembre, oktober, novembre, décembre

Comment puis-je fais ça?

Br, Wolfy

Répondre

3

La solution complète la plus compacte serait quelque chose comme:

CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI"); 
_items = _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo)).ToList(); 

Cédant cette liste ci-dessus à lui-même. Si vous allez le faire, alors vous pourriez gagner en performance pour une augmentation relativement faible dans la complexité du code par le tri en place:

CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI"); 
_items.Sort((x, y) => DateTime.ParseExact("01 " + x, @"dd MMMM yyyy\.\c\s\v",sloveneCultureInfo).CompareTo(DateTime.ParseExact("01 " + y, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo))); 

(Si vous utilisez ce sur un système dans un environnement linguistique slovène, puis vous pouvez utiliser le CurrentCulture plutôt que le sloveneCultureInfo construit ci-dessus).

D'autres améliorations peuvent être faites en utilisant une classe de comparaison plutôt qu'un lambda, et en effet la comparaison elle-même pourrait être plus efficace que celle ci-dessus, mais je ne m'inquiéterais pas à moins que le genre .

Éditer: Un détail sur ce qui fonctionne ici.

Il existe deux manières pratiques de trier une liste (strictement, une façon de trier une liste et une autre de tri n'importe quel IEnumerable ou IQueryable).

OrderBy prend un paramètre qui calcule une clé de tri, et renvoie un IOrderedEnumerable<T> (ou IOrderedQueryable<T> à partir de maintenant je vais ignorer le fait que cela peut être fait sur IQueryable ainsi, le principe est le même). Cette classe est celle qui, dans la plupart des cas, agit comme un IEnumerable<T> qui est trié par la clé, à la seule différence que si vous faites un ThenBy suivant, il reste ordonné par la première clé, et vous permet donc d'avoir plusieurs étapes commandes.

Ainsi, dans le code:

var x = _items.OrderBy(SomeMethod); 

SomeMethod renverra des valeurs qui peuvent être triées, et sur la base de cela, x sera donné une IOrderedEnumerable<T> triée en conséquence. Par exemple si SomeMethod a pris une chaîne et a retourné la longueur de la chaîne, alors nous pourrions l'utiliser pour trier une liste de chaînes par longueur.

Si nous allions ensuite parcourir les _items, alors c'est notre travail. Si nous voulions avoir une nouvelle liste alors nous pourrions appeler Tolist() sur les résultats, et cela renverrait une telle liste.

L'autre approche, qui ne fonctionne que sur les listes, consiste à appeler Sort(). Il existe une forme sans paramètre qui trie simplement selon la comparaison par défaut du type en question (s'il y en a un), un formulaire qui prend un objet IComparer<T> (plus impliqué que nécessaire dans la plupart des cas, mais parfois très utile) et un formulaire prend un délégué ou un lambda (strictement il prend un délégué, mais nous pouvons utiliser un lambda pour créer ce délégué). Ce délégué doit recevoir deux valeurs du type des éléments de la liste et renvoyer un nombre négatif si le premier doit être trié plus tôt que le dernier, zéro s'ils sont équivalents et un nombre positif sinon.

Sort() modifie la liste son fait. Cela a un gain d'efficacité, mais est clairement désastreux si vous avez besoin de garder l'ordre de tri d'origine aussi. D'accord, jusqu'à présent, nous avons deux façons de trier une liste. Les deux ont besoin d'un moyen de transformer vos noms de fichiers en quelque chose qui peut être trié.

Étant donné que les noms de fichiers se rapportent aux dates et que les dates sont déjà triables, l'approche la plus sensée consiste à obtenir ces dates.

Nous ne pouvons pas créer une date directement à partir du nom du fichier, car Avgust 2010 est pas une date, juste une valeur de mois l'année et il n'y a pas une classe d'année mois dans la BCL (il peut y avoir dans d'autres bibliothèques mais ne lions pas les lys).

Cependant, chaque mois a un premier jour, et donc nous pouvons créer une date valide à partir de "01" concaténé avec le nom de fichier. Donc, notre première étape est de dire que nous allons agir sur "01 " + fnfn est le nom du fichier.

Cela nous donne des chaînes sous la forme de par ex. "01 Avgust 2010.csv". En examinant le fonctionnement de l'analyse de date, nous savons que nous pouvons utiliser dd pour la date à deux chiffres, MMMM pour le nom complet du mois dans la langue appropriée (slovène dans ce cas) et yyyy pour l'année complète. Nous avons juste besoin d'ajouter \.\c\s\v pour signifier qu'il sera suivi d'un ".csv" que nous n'analyserons pas, et nous sommes prêts. Nous pouvons donc transformer le nom du fichier en le premier de son mois avec DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI")) où fn est le nom du fichier. Pour en faire une expression lambda, nous utilisons fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI")).

La première approche est maintenant terminée, nous appelons _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI"))) et dépendons de OrderBy en sachant comment trier les valeurs DateTime. Pour la deuxième approche, nous devons prendre deux valeurs et les comparer nous-mêmes. Nous pouvons le faire en appelant CompareTo() sur le DateTime retourné par ParseExact.

Enfin, nous déplaçons la construction CultureInfo pour initialiser une variable appelée sloveneCultureInfo pour éviter les appels inutiles aux multiples créations d'essentiellement le même objet.

+0

Ajout d'une explication de pourquoi cela fonctionne. –

+0

Merci, cela m'a vraiment aidé à comprendre cela ... – Wolfy

4

Il semble que vous devriez écrire une méthode pour analyser un nom de fichier dans un DateTime. Par exemple, il peut enlever l'extension, diviser le reste par l'espace, puis analyser la deuxième partie comme l'année et rechercher le nom du mois dans un tableau.

Une fois que vous avez cette méthode, vous pouvez juste faire:

_items = _items.OrderBy(FilenameToDate).ToList(); 
+0

Non, c'est vrai - c'est un groupe de méthodes. – Archeg

+0

@generic Non, c'est vrai. L'intention est de passer le délégué dans OrderBy afin qu'il puisse être appelé pendant le tri, de ne pas l'appeler un résultat indéfini à OrderBy – AHM

+0

Woops, oui. Il est tôt. – GenericTypeTea