Compruebe si un IEnumerable contiene todos los elementos de otro IEnumerable


102

¿Cuál es la forma más rápida de determinar si un IEnumerable contiene todos los elementos de otro IEnumerable al comparar un campo / propiedad de cada elemento en ambas colecciones?


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
¿En qué sentido están tus listas? ¿Quiere comprobar si todos los elementos de list1 están en la lista 2 o si todos los elementos de list2 están en la lista 1?
Mark Byers

Respuestas:


138

No existe una "forma rápida" de hacer esto a menos que realice un seguimiento y mantenga algún estado que determine si todos los valores de una colección están contenidos en otra. Si solo tienes IEnumerable<T>que trabajar en contra, usaría Intersect.

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

El rendimiento de este debe ser muy razonable, ya que Intersect()se enumerará sobre cada lista una sola vez. Además, la segunda llamada a Count()será óptima si el tipo subyacente es un en ICollection<T>lugar de solo un IEnumerable<T>.


Hice algunas pruebas y este método parece funcionar más rápido que los demás. Gracias por el consejo.
Brandon Zacharie

2
Esto no funciona si hay duplicados en la lista. Por ejemplo, comparar una matriz de caracteres de 441 y 414 devuelve 41 y, por lo tanto, el recuento falla.
Juan

69

También puede usar Excepto para eliminar de la primera lista todos los valores que existen en la segunda lista y luego verificar si se han eliminado todos los valores:

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

Este método tenía la ventaja de no requerir dos llamadas a Count ().


Esto también es bueno para averiguar qué hay en List1 pero no en List2;
Homer

16
Esto funciona en situaciones en las que list1 tiene valores duplicados. La respuesta aceptada no lo hace.
dbc

23

C # 3.5+

Utilizando Enumerable.All<TSource>para determinar si todos los elementos de List2 están contenidos en List1:

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

Esto también funcionará cuando list1 contenga incluso más que todos los elementos de list2.


10
¡Ay de las implicaciones de rendimiento de una Contains()llamada dentro de una All()llamada!
Kent Boogaart

También puede moverlo al método de grupo: bool hasAll = list2Uris.All (list1Uris.Contains);
jimpanzer

En el caso de los tipos IEnumerable <T>, esta solución proporcionará un rendimiento n * m.
Dmitriy Dokshin

5
Taquigrafía: bool hasAll = list2Uris.All(list1Uris.Contains);
Iluminador

3

La respuesta de Kent es buena y breve, pero la solución que proporciona siempre requiere iteración sobre toda la primera colección. Aquí está el código fuente:

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;
    }
}

Eso no siempre es obligatorio. Entonces, aquí está mi solución:

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;
}

En realidad, deberías pensar en usar ISet<T>( HashSet<T>). Contiene todos los métodos de configuración necesarios. IsSubsetOfen tu caso.


2

el operador de Linq SequenceEqual también funcionaría (pero es sensible a que los elementos del enumerable estén en el mismo orden)

return list1Uris.SequenceEqual(list2Uris);

2

La solución marcada como la respuesta fallaría en el caso de repeticiones. Si su IEnumerable solo contiene valores distintos, pasará.

La siguiente respuesta es para 2 listas con repeticiones:

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

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

Esta no es una buena solución, ya que elimina todos los duplicados y no los compara.
Juan

2

Debería utilizar HashSet en lugar de Array.

Ejemplo:

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

Referencia

La única limitación de HasSet es que no podemos obtener elemento por índice como Lista ni obtener elemento por clave como Diccionarios. Todo lo que puede hacer es enumerarlos (para cada uno, mientras, etc.)

Por favor, avíseme si eso funciona para usted


-2

puede utilizar este método para comparar dos listas

    //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;
    }

Casi la misma respuesta se dio 3 años antes: stackoverflow.com/a/16967827/5282087
Dragomok
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.