2009-10-15 6 views

Répondre

3

Il est préférable d'utiliser

int x = points.Min(p => p.X); 
var result = points.First(p => p.X == x); 

car cela élimine la nécessité de trier cette liste (par exemple, il est O(n) par opposition à, disons, O(n log n)). En outre, il est plus clair que d'utiliser OrderBy et First.

Vous pouvez même écrire une méthode d'extension comme suit:

static class IEnumerableExtensions { 
    public static T SelectMin<T>(this IEnumerable<T> source, Func<T, int> selector) { 
     if (source == null) { 
      throw new ArgumentNullException("source"); 
     } 

     int min = 0; 
     T returnValue = default(T); 
     bool flag = false; 

     foreach (T t in source) { 
      int value = selector(t); 
      if (flag) { 
       if (value < min) { 
        returnValue = t; 
        min = value; 
       } 
      } 
      else { 
       min = value; 
       returnValue = t; 
       flag = true; 
      } 
     } 

     if (!flag) { 
      throw new InvalidOperationException("source is empty"); 
     } 

     return returnValue; 
    } 

Utilisation:

IEnumerable<Point> points; 
Point minPoint = points.SelectMin(p => p.X); 

Vous pouvez généraliser à vos besoins. L'avantage de ceci est qu'il évite potentiellement de parcourir deux fois la liste.

+0

À moins que ma logique ne m'échoue .Min() doit inspecter chaque élément? Ensuite, il renvoie cette valeur. Tout d'abord recommence à parcourir la même liste, jusqu'à ce qu'il trouve le premier point avec cette valeur X. Pire des cas c'est 2 * n? –

+0

J'ai ajouté une méthode d'extension pour résoudre ce problème. Deuxièmement, '2 * n' est' O (n) '. – jason

2

Ce qui suit doit être le plus rapide, mais pas la plus jolie façon de le faire:

public static T MinValue<T>(this IEnumerable<T> e, Func<T, int> f) 
{ 
    if (e == null) throw new ArgumentException(); 
    var en = e.GetEnumerator(); 
    if (!en.MoveNext()) throw new ArgumentException(); 
    int min = f(en.Current); 
    T minValue = en.Current; 
    int possible = int.MinValue; 
    while (en.MoveNext()) 
    { 
     possible = f(en.Current); 
     if (min > possible) 
     { 
      min = possible; 
      minValue = en.Current; 
     } 
    } 
    return minValue; 
} 

Je ne compris l'extension int, mais il est trivial de faire d'autres.

Édition: modifié par Jason.

+2

Commentaire: 'Count 'est mauvais à invoquer à moins que cela ne soit absolument nécessaire sur un' IEnumerable' car cela provoque la marche de la liste (dans le cas général). Si marcher dans 'IEnumerable' est cher, c'est mauvais. – jason

+0

Un autre commentaire: Notez que dans la boucle 'while' vous calculez' f (en.Current) 'sur l'élément courant deux fois. Si 'f' est cher alors ce n'est pas optimal. – jason

0

Pour ceux qui cherchent à le faire aujourd'hui, MoreLinq est une bibliothèque disponible par NuGet qui comprend l'opérateur fourni par les autres réponses, ainsi que plusieurs autres opérations utiles ne sont pas présents dans le cadre.

Questions connexes