samedi 17 novembre 2007

Trier et filtrer selon des prédicats

La documentation MSDN est pour le moins sybilline sur comment utiliser le tri avec un prédicat (c'est à dire, en précisant le paramètre de tri dans l'argument). C'est un bon premier exemple pour utiliser les delegates (méthodes anonymes).

Premier cas de tri : trier une Liste générique à l'envers


Supposons par exemple que l'on veuille trier les élements d'une List<T> à l'envers. Ici, T implémentera IComparable, et aura donc la méthode public int CompareTo(T element), qui renvoie 0 si les deux élements sont égaux, -1 si l'élement element est plus grand que notre instance, et 1 sinon.

La méthode CompareTo est appelée par défaut. Toutefois, on veut trier à l'envers. La solution sale et à éviter est :
1/ Trier à l'endroit
2/ Inverser la liste

En effet, on peut faire autrement et de manière beaucoup plus jolie, en utilisant un delegate qui implémente (implicitement) IComparer.

A supposer que la liste soit nommée maListe, on fera :


maListe.Sort(delegate(T el1, T el2) {
return - el1.CompareTo(el2);
});


Deuxième cas de tri : trier des objets selon leurs champs


On peut aussi imaginer le cas d'une collection d'objets de classe MaLigne qui comportent chacun trois champs de type String, nommés s1, s2, s3.
Cette collection peut être la représentation logique d'un tableau à 3 colonnes, par exemple. On veut pouvoir trier en ordre ascendant la liste selon s1, s2, ou s3.
Pour ce faire, on va encore utiliser un delegate et le fait que les String soient comparables les unes avec les autres. A supposer que notre collection soit une List<MaLigne> de nom monTableau


// trier selon s1
monTableau.Sort(delegate(MaLigne el1,
MaLigne el2)
{
return el1.s1.CompareTo(el2.s1);
});
// trier selon s2
monTableau.Sort(delegate(MaLigne el1,
MaLigne el2)
{
return el1.s2.CompareTo(el2.s2);
});
// trier selon s3
monTableau.Sort(delegate(MaLigne el1,
MaLigne el2)
{
return el1.s3.CompareTo(el2.s3);
});


Pour trier dans l'ordre inverse, il suffit de mettre un petit "-" juste après le return. Comme vous pouvez le voir, les facilités de tri sont très importantes en C#.

Filtrer une collection



La méthode préférée pour filtrer une collection est d'utiliser FindAll. FindAll prend un prédicat (une condition à vérifier) et retourne une liste chaînée (une List<T>) contenant les élements de la liste qui correspondent à ce prédicat. Le prédicat devra retourner cette fois un booléen selon que l'élement corresponde ou pas.

Un exemple : supposons que l'on ait une List<CompteEnBanque> comptesEnBanque. Un CompteEnBanque a, entre autres, un champ Solde, de type double (signé).
Pour obtenir la liste des comptes à découvert, on procède ainsi :

List<CompteEnBanque> aDecouvert =
comptesEnBanque.FindAll(delegate(CompteEnBanque c)
{
return c.Solde < 0;
});


Quelques remarques


Vous remarquerez que les delegate anonymes n'ont pas de signature. On n'écrit pas int delegate(Foo f, Foo g){}.
On peut aussi trier et filtrer selon des méthodes définies et nommées. Je vous renvoie à la documentation MSDN pour des exemples.

Aucun commentaire: