2010-07-18 13 views
65

Qual è il modo più veloce per determinare se un IEnumerable contiene tutti gli elementi di un altro IEnumerable quando si confronta un campo/proprietà di ciascun elemento in entrambe le raccolte?Verificare se un IEnumerable contiene tutti gli elementi di un altro IEnumerable


public class Item 
{ 
    public string Value; 

    public Item(string value) 
    { 
     Value = value; 
    } 
} 

//example usage 

Item[] List1 = {new Item("1"),new Item("a")}; 
Item[] List2 = {new Item("a"),new Item("b"),new Item("c"),new Item("1")}; 

bool Contains(IEnumerable<Item> list1, IEnumerable<Item>, list2) 
{ 
    var list1Values = list1.Select(item => item.Value); 
    var list2Values = list2.Select(item => item.Value); 

    return //are ALL of list1Values in list2Values? 
} 

Contains(List1,List2) // should return true 
Contains(List2,List1) // should return false 
+1

Quali viceversa sono le vostre liste? Vuoi controllare se tutti gli elementi di list1 sono nella lista 2 o che tutti gli elementi di list2 sono nella lista 1? –

risposta

94

Non esiste un "metodo rapido" per eseguire questa operazione se non si tiene traccia e si mantiene uno stato che determina se tutti i valori in una raccolta sono contenuti in un'altra. Se hai solo IEnumerable<T> per lavorare contro, vorrei usare Intersect.

var allOfList1IsInList2 = list1.Intersect(list2).Count() == list1.Count(); 

Le prestazioni di questo dovrebbe essere molto ragionevole, dal momento Intersect() enumererà sopra ogni lista solo una volta. Inoltre, la seconda chiamata a Count() sarà ottimale se il tipo sottostante è un ICollection<T> anziché solo un IEnumerable<T>.

+0

Ho eseguito alcuni test e questo metodo sembra essere eseguito più velocemente degli altri. Grazie per il consiglio. –

+0

Penso che tu intenda 'var allOfList2IsInList1 = list1.Intersect (list2) .Count() == list2.Count();' – dan

+2

@fsmmu: no, non l'ho fatto. La prima chiamata rileva quanti elementi sono presenti nell'intersezione degli elenchi 1 e 2. La seconda chiamata rileva quanti elementi sono presenti nell'elenco 1. Se tali numeri sono uguali, allora tutto l'elenco 1 si trova nell'elenco 2, come da OP domanda. –

2

l'operatore SequenceEqual Linq avrebbe funzionato anche (ma è sensibile agli elementi del enumerabili che sono nello stesso ordine)

return list1Uris.SequenceEqual(list2Uris); 
18

C# 3.5+

Utilizzando Enumerable.All<TSource> per determinare se tutti gli elementi di List2 sono contenuti in List1:

bool hasAll = list2Uris.All(itm2 => list1Uris.Contains(itm2)); 

Ciò funzionerà anche quando list1 contiene anche più di tutte le voci di list2.

+8

Ouch alle implicazioni di prestazioni di una chiamata 'Contains()' all'interno di una chiamata 'All()'. –

+0

Inoltre è possibile spostarlo nel metodo di gruppo: bool hasAll = list2Uris.All (list1Uris.Contains); – jimpanzer

+0

I caso di IEnumerable tipi questa soluzione fornirà prestazioni n * m. –

31

Si potrebbe anche usare Fatta eccezione per rimuovere dal primo elenco di tutti i valori che esistono nel secondo elenco, e poi verificare se sono stati rimossi tutti i valori:

var allOfList1IsInList2 = !list1.Except(list2).Any(); 

Questo metodo ha il vantaggio di non richiedere due chiama Count().

+0

Questo è anche un bene per scoprire cosa c'è in List1 ma non in List2; – Homer

+5

Funziona in situazioni in cui list1 ha valori duplicati. La risposta accettata no. – dbc

4

La soluzione contrassegnata come risposta fallirebbe nel caso di ripetizioni. Se il tuo IEnumerable contiene solo valori distinti, passerà.

La risposta che segue è per 2 liste con ripetizioni:

 int aCount = a.Distinct().Count(); 
     int bCount = b.Distinct().Count(); 

     return aCount == bCount && 
       a.Intersect(b).Count() == aCount; 
3

risposta di Kent è fine e corto, ma la soluzione che fornisce l'iterazione richiede sempre su tutta la prima collezione. Ecco il codice sorgente:

public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) 
{ 
    if (first == null) 
     throw Error.ArgumentNull("first"); 
    if (second == null) 
     throw Error.ArgumentNull("second"); 
    return Enumerable.IntersectIterator<TSource>(first, second, comparer); 
} 

private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) 
{ 
    Set<TSource> set = new Set<TSource>(comparer); 
    foreach (TSource source in second) 
     set.Add(source); 
    foreach (TSource source in first) 
    { 
     if (set.Remove(source)) 
      yield return source; 
    } 
} 

Non sempre è necessario. Quindi, ecco la mia soluzione:

public static bool Contains<T>(this IEnumerable<T> source, IEnumerable<T> subset, IEqualityComparer<T> comparer) 
{ 
    var hashSet = new HashSet<T>(subset, comparer); 
    if (hashSet.Count == 0) 
    { 
     return true; 
    } 

    foreach (var item in source) 
    { 
     hashSet.Remove(item); 
     if (hashSet.Count == 0) 
     { 
      break; 
     } 
    } 

    return hashSet.Count == 0; 
} 

In realtà, si dovrebbe pensare di utilizzare ISet<T> (HashSet<T>). Contiene tutti i metodi impostati richiesti. IsSubsetOf nel tuo caso.

-1

è possibile utilizzare questo metodo per confrontare due elenco

//Method to compare two list 
    private bool Contains(IEnumerable<Item> list1, IEnumerable<Item> list2) 
    { 
     bool result; 

     //Get the value 
     var list1WithValue = list1.Select(s => s.Value).ToList(); 
     var list2WithValue = list2.Select(s => s.Value).ToList(); 

     result = !list1WithValue.Except(list2WithValue).Any(); 

     return result; 
    } 
+0

Quasi la stessa risposta è stata data 3 anni prima: http://stackoverflow.com/a/16967827/5282087 – Dragomok

0

Si consiglia di utilizzare HashSet invece di Array.

Esempio:

List1.SetEquals(List2); //returns true if the collections contains exactly same elements no matter the order they appear in the collection 

Reference

L'unica limitazione Hasset è che non possiamo avere voce in base all'indice come Lista né avere voce da chiave come dizionari. Tutto quello che puoi fare è elencare (per ciascuno, mentre, ecc)

Per favore fatemi sapere se funziona per voi

Problemi correlati