2010-12-08 17 views
0

Je travaille avec un dictionnaire IEnumerable (data.Tags) dans une liste (masterList).LINQ: Comment retourner champ différent de celui utilisé par min()

Voici une question typique que je fais (qui fonctionne):

var tagList = 
    (from data in masterList 
    from tag in data.Tags 
    where tag.id == 0x10 
    select tag).Distinct(new TagComparer()); 

La classe tag a des champs Id, Value et TranslatedValue. Je veux rechercher basé sur Id, utilisez Value pour déterminer quel est le minimum, puis renvoyer le TranslatedValue (au lieu de Value).

Toutes mes tentatives jusqu'ici jeter un ArgumentException, comme ceci:

var tagList = 
    (from data in masterList 
    from tag in data.Tags 
    where tag.id == 0x10 
    select new 
    { 
     tag.Value, 
     tag.TranslatedValue 
    }; 
return tagList.Min().TranslatedValue; 

Y at-il une solution élégante à ce sujet?

+0

Qu'est-ce '.min()' devrait être? Comment le compilateur devrait-il le déterminer? –

+0

Comme indiqué ci-dessus, _use 'Value' pour déterminer quel est le minimum_. Quant à comment, c'est pourquoi je demande :) – glenneroo

Répondre

3

Vous pouvez utiliser OrderBy commander les éléments dans l'ordre croissant (mettre la valeur minimale à la première position), Select pour obtenir votre valeur traduite, puis First de prendre cet élément minimum:

var minVal = (from data in masterList 
      from tag in data.Tags 
      where tag.id == 0x10 
      select tag) 
      .Distinct(new TagComparer()) 
      .OrderBy(t => t.Value) // lowest value will be first in the list 
      .First() // take the first element, which is the min 
      .TranslatedValue 
1

Vous peut implémenter IComparable<T> pour votre classe d'étiquettes et baser la comparaison sur la propriété Value. Ensuite, au lieu de sélectionner le minimum Value, vous pouvez sélectionner l'étiquette minimale et prendre le TranslatedValue à partir de cela.

code pour démontrer ce que je veux dire:

class tag : IComparable<tag> 
{ 
    public int Id { get; set; } 

    public int Value { get; set; } 

    public int TranslatedValue { get; set; } 

    public int CompareTo(tag other) 
    { 
     if (other.Value > this.Value) 
      return -1; 
     if (other.Value < this.Value) 
      return 1; 
     return 0; 
    } 

    public override string ToString() 
    { 
     return string.Format("Id:{0}, Value: {1}, TranslatedValue: {2}", Id, Value, TranslatedValue); 
    } 
} 

Qui Changent votre classe d'étiquette à mettre en œuvre IComparable<T>. Pour l'utiliser pour obtenir le TranslatedValue il est aussi simple que:

int minTranslatedValue = tags.Min().TranslatedValue; 

tags est une sorte de collection IEnumerable<Tag>.

0

Vous devriez être en mesure de trier par une propriété, prendre le premier élément, et obtenir une autre propriété:

return 
    masterList 
    .SelectMany(data => data.Tags) 
    .Where(tag => tag.id == 0x10) 
    .OrderBy(tag => tag.Value) 
    .First() 
    .TranslatedValue; 
+0

OrderByDescending ne mettrait pas la valeur maximale dans la première position (je pense qu'il veut OrderBy qui fait l'ordre ascendant). –

+0

@Justin Niessner: Oui, vous avez raison. Je pensais à l'envers. : P Il devrait bien sûr être 'OrderBy'. – Guffa

1

Ma réponse quand j'ai rencontré ce problème exact avant était un peu plus compliqué que les autres ici, mais cela fonctionne très bien dans ces situations et est un peu plus performant; développer une méthode d'extension ObjectWithMin() qui retournera l'objet entier qui contient la valeur minimale d'une projection spécifiée. Voici un algorithme de base; il insiste aussi sur la dénombrable une seule fois et exécute linéaire au lieu de nlogn-temps:

public static T ObjectWithMin<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection) 
     where TResult : IComparable<TResult> 
    { 
     if (elements == null) throw new ArgumentNullException("elements", "Sequence is null."); 
     if (!elements.Any()) throw new ArgumentException("Sequence contains no elements."); 

     var seed = elements.Select(t => new { Object = t, Projection = projection(t) }).First(); 

     return elements.Aggregate(seed, 
            (s, x) => 
            projection(x).CompareTo(s.Projection) < 0 
             ? new {Object = x, Projection = projection(x)} 
             : s 
      ).Object; 
    } 

Utilisation:

var tagList = 
    (from data in masterList 
    from tag in data.Tags 
    where tag.id == 0x10 
    select new 
    { 
     tag.Value, 
     tag.TranslatedValue 
    }; 
return tagList.ObjectWithMin(x=>x.Value).TranslatedValue;