2010-09-10 8 views
10

une méthode d'extension sur une collection nommée MeasurementCollection vérifie si la propriété Template.Frequency (Enum) de chaque élément a la même valeur.Vérifiez si tous les éléments d'une collection ont la même valeur

public static bool IsQuantized(this MeasurementCollection items) 
    { 
     return (from i in items 
       select i.Template.Frequency) 
       .Distinct() 
       .Count()==1; 

    } 

modifier informations sur les classes sous-jacentes

MeasurementCollection : ICollection<IMeasurement> 

    IMeasurement 
    { 
    IMeasurementTemplate Template { get; }   
    ...... 
    } 

Est-ce une bonne approche ou est-il une solution plus facile déjà Linq? Cette méthode sera utilisée intensément dans l'application.

Avez-vous des conseils à prendre avec moi, de retour à la planche à dessin?

+1

Qu'est-ce que vous êtes la condition est importante lisibilité? la vitesse est-elle importante? quel est le scénario habituel. Qu'ils sont identiques ou qu'ils ne sont pas les mêmes? –

+1

Basé sur le tag #arrays que vous avez mis sur cette question, pouvons-nous supposer que votre objet MeasurementCollection est stocké en tant que tableau ou ArrayList? Pouvons-nous obtenir un '.Count' d'un MeasurementCollection sans trop de frais généraux? Quelle réponse est correcte dépendra de savoir plus sur votre collection sous-jacente. – StriplingWarrior

+0

Pourquoi vous ne voulez pas ajouter cette méthode (avec n'importe quelle implémentation) dans MeasurementCollection? Pourquoi utilisez-vous la méthode d'extension pour cela? Si cette fonctionnalité fait partie de cette abstraction particulière, montrez-la explicitement. –

Répondre

8

Edit: pour répondre aux préoccupations de Timwi environ 3 agents recenseurs:

bool same = <your default> ; 
var first = items.FirstOrDefault(); 
if (first != null) // assuming it's a class 
{ 
    same = items.Skip(1).All(i => i.Template.Frequency == first.Template.Frequency); 
} 

qui utilise encore 2 agents recenseurs. Pas un problème pour la List<> moyenne, mais pour une requête DB, il pourrait payer pour utiliser le moins lisible:

bool same = <your default> ; 
Item first = null; 

foreach(var item in items) 
{ 
    if (first == null) 
    { 
     first = item; 
     same = true; 
    } 
    else 
    { 
     if (item.Template.Frequency != first.Template.Frequency) 
     { 
      same = false; 
      break; 
     } 
    } 
} 
+0

votre code ne fait pas la même comparaison que les OP. Il compare une propriété d'une propriété. Le vôtre fait une comparaison de référence –

+1

@Rune: Je vais ajouter cela pour l'exhaustivité mais ce n'est pas une différence fondamentale. A moins que certaines propriétés ne soient nulles ce qui en ferait un gâchis. –

+0

Ceci est le même que le 'Trick # 2' dans [ma deuxième réponse] (http://stackoverflow.com/questions/3687597/check-if-all-items-in-a-collection-have-the-same -value/3687724 # 3687724). Il instancie deux énumérateurs et évalue deux fois le premier élément - et si vous ne voulez pas qu'il soit lancé, vous devez en avoir un troisième pour vérifier tout d'abord 'Any()'. Cela peut être un problème de performances si la collection est paresseuse et que le premier élément prend du temps à calculer. – Timwi

2

Ce serait plus rapide comme ceci:

public static bool IsQuantized(this MeasurementCollection items) 
{ 
    if(items == null || items.Count == 0) 
     return true; 

    var valueToCompare = items.First().Template.Frequency; 

    return items.All(i => i.Template.Frequency == valueToCompare); 
} 

Il retournera faux sur la première La fréquence du template de l'objet est différente, alors que dans votre code, l'algorithme passe la totalité de la collection.

+0

Cela suppose que la collection est indexable. – Timwi

+0

Là, l'a réparé! –

+0

L'OP a mis une balise #arrays sur cette question, ce qui peut indiquer que MeasurementCollection est implémenté comme un tableau ou une structure similaire. – StriplingWarrior

4

Le premier d'un conseil linq général. Si vous voulez savoir s'il y en a exactement une dans une collection, utilisez Single() ou SingleOrDefault(). Count va potentiellement itérer toute la collection qui est plus que nécessaire car vous pouvez renflouer s'il y en a deux.

public static bool IsQuantized(this MeasurementCollection items) 
     { 
      var first = items.FirstOrDefault(); 
      return first != null && items.Skip(1).All(i => first.Template.Frequency == i.Template.Frequency)); 
     } 
+0

Pourquoi la downvote? –

+0

Ce n'est pas mon -1 mais votre code semble ne pas très bien fonctionner avec une collection vide. Et vous étiez le perfectionniste. –

+0

@Henk vous avez raison, j'ai donc mis à jour ma réponse. Je dirais cependant qu'il est perfectionniste de commenter une implémentation qui dans la plupart des cas donnerait une réponse incorrecte. Je ne vois pas comment louer votre approche générale pour sa lisibilité est perfectionniste :) –

7

Vous pourriez trouver la première valeur et vérifiez si tous les autres sont différents, cela évitera d'avoir à eval toute la collection (à moins que la valeur différente unique est le dernier)

public static bool IsQuantized(this MeasurementCollection items) 
{ 
    if(!items.Any()) 
     return false; //or true depending on your use case 

    //might want to check that Template is not null, a bit a violation of level of demeter, but just an example 
    var firstProp = item.First().Template.Frequency; 

    return !items.Any(x=> x.Template.Frequency != firstProp); 

} 
3

I J'ai eu un peu d'inspiration et j'ai pensé à une solution avec seulement la vitesse à l'esprit. Ce n'est vraiment pas lisible (ce que je préfère habituellement) mais les caractéristiques en matière de vitesse devraient être plutôt bonnes.

Le pire des cas est le même pour la plupart des autres implémentations O (n) mais il est hautement improbable car toute la première moitié des éléments doit être égale à et pas égal à la valeur dans la première moitié. et nécessiterait le même nombre de comparaisons qu'une recherche linéaire. Dans la plupart des autres cas, le premier dans un endroit aléatoire, il faudra deux fois moins de comparaisons que le linéaire. Dans le cas où les valeurs sont en paires. Donc cet item [0] == item [1] et item [2] == item [3] et item [0]! = Item [2] (et similaire) alors la recherche linéaire sera plus rapide. En général soit avec des données aléatoires ou quelques impairs une fois cela devrait être plus rapide qu'une recherche linéaire

public static bool AllSame<T>(this IEnumerable<T> source, 
           IEqualityComparer<T> comparer = null) 
     { 
      if (source == null) 
       throw new ArgumentNullException("source cannot be null.", "source"); 

      if (comparer == null) 
       comparer = EqualityComparer<T>.Default; 
      var enumerator = source.GetEnumerator(); 

      return source.Zip(comparer); 
     } 

     private static bool Zip<T>(this IEnumerable<T> sequence, IEqualityComparer<T> comparer) 
     { 
      var result = new List<T>(); 
      var enumerator = sequence.GetEnumerator(); 
      while (enumerator.MoveNext()) 
      { 
       var first = enumerator.Current; 
       result.Add(enumerator.Current); 
       if (enumerator.MoveNext()) 
       { 
        if (!comparer.Equals(first, enumerator.Current)) 
        { 
         return false; 
        } 
       } 
       else 
       { 
        break; 
       } 
      } 
      return result.Count == 1 ? true : result.Zip(comparer); 
     } 

avec une optimisation des appels de queue celui-ci utilise la mémoire supplémentaire (avec le pire des cas d'une quantité de mémoire proche de la quantité de mémoire utilisée pour la source d'origine). La pile d'appels ne devrait pas être trop profonde car aucune implémentation concrète IEnumerable (du moins à ce que je sache) ne peut contenir plus d'éléments int.MaxValue. ce qui nécessiterait un maximum de 31 récursions.

0

Je l'ai fait de cette façon:

public static bool Same<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) 
    { 
     var val = source.Select(keySelector).FirstOrDefault(); 

     return source.Select(keySelector).All(a => Object.Equals(a, val)); 
    } 

Utilisation:

ddlStatus.AppendDataBoundItems = true; 
ddlStatus.Items.Add(new ListItem("<Mixed>", "-1")); 
ddlStatus.DataSource = ctx.Status.OrderBy(s => s.AssetStatus).ToList(); 
ddlStatus.DataTextField = "AssetStatus"; 
ddlStatus.DataValueField = "id"; 
ddlStatus.SelectedValue = Assets.Same(a => a.AssetStatusID) ? Assets.FirstOrDefault().AssetStatusID.ToString() : "-1"; 
ddlStatus.DataBind(); 

C'est une liste déroulante avec une liste des statuts disponibles. Le formulaire modifie plusieurs actifs. La liste déroulante doit savoir si les actifs ont tous la même valeur ou non. My Same extension fait cela.

0

Je suggère la solution suivante:

private static bool IsSameCollections(ICollection<> collection1, ICollection<> collection2) 
     { 
      return collection1.Count == collection2.Count && 
    (collection1.Intersect(collection2).Count() == collection1.Count); 
     } 
Questions connexes