Kiểm tra xem một IEnumerable có chứa tất cả các phần tử của IEnumerable khác hay không


102

Cách nhanh nhất để xác định xem một IEnumerable có chứa tất cả các phần tử của IEnumerable khác hay không khi so sánh một trường / thuộc tính của mỗi phần tử trong cả hai tập hợp?


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
Danh sách của bạn nằm ở vòng nào? Bạn có muốn kiểm tra xem tất cả các mục trong danh sách1 có trong danh sách 2 hay tất cả các mục trong danh sách2 đều nằm trong danh sách 1 không?
Mark Byers

Câu trả lời:


138

Không có "cách nhanh" nào để làm điều này trừ khi bạn theo dõi và duy trì một số trạng thái xác định xem tất cả các giá trị trong một bộ sưu tập có được chứa trong một bộ sưu tập khác hay không. Nếu bạn chỉ phải IEnumerable<T>làm việc chống lại, tôi sẽ sử dụng Intersect.

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

Hiệu suất của điều này phải rất hợp lý, vì Intersect()sẽ liệt kê trên mỗi danh sách chỉ một lần. Ngoài ra, lệnh gọi thứ hai tới Count()sẽ là tối ưu nếu kiểu cơ bản là một ICollection<T>thay vì chỉ là một IEnumerable<T>.


Tôi đã thực hiện một số thử nghiệm và phương pháp này có vẻ chạy nhanh hơn các phương pháp khác. Cảm ơn vì tiền hỗ trợ.
Brandon Zacharie

2
Điều này không hoạt động nếu có các bản sao trong danh sách. Ví dụ: so sánh mảng char của 441 và 414 trả về 41 và do đó việc đếm không thành công.
John

69

Bạn cũng có thể sử dụng Ngoại trừ để xóa khỏi danh sách đầu tiên tất cả các giá trị tồn tại trong danh sách thứ hai, sau đó kiểm tra xem tất cả các giá trị đã bị xóa chưa:

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

Phương thức này có ưu điểm là không yêu cầu hai lệnh gọi tới Count ().


Điều này cũng tốt để tìm ra những gì có trong List1 nhưng không có trong List2;
Homer

16
Điều này hoạt động trong các trường hợp list1 có các giá trị trùng lặp. Câu trả lời được chấp nhận là không.
dbc

23

C # 3.5+

Sử dụng Enumerable.All<TSource>để xác định xem tất cả các mục List2 có trong List1 hay không:

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

Điều này cũng sẽ hoạt động khi list1 chứa nhiều hơn tất cả các mục của list2.


10
Rất tiếc về hiệu suất của một Contains()cuộc gọi trong một All()cuộc gọi.
Kent Boogaart,

Ngoài ra, bạn có thể chuyển nó sang phương thức nhóm: bool hasAll = list2Uris.All (list1Uris.Contains);
jimpanzer

Trường hợp của IEnumerable <T>, giải pháp này sẽ cung cấp hiệu suất n * m.
Dmitriy Dokshin

4
Tốc ký: bool hasAll = list2Uris.All(list1Uris.Contains);
Đèn chiếu sáng

3

Câu trả lời của Kent là tốt và ngắn gọn, nhưng giải pháp mà anh ấy cung cấp luôn yêu cầu lặp lại toàn bộ bộ sưu tập đầu tiên. Đây là mã nguồn:

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

Điều đó không phải lúc nào cũng bắt buộc. Vì vậy, đây là giải pháp của tôi:

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

Trên thực tế, bạn nên nghĩ đến việc sử dụng ISet<T>(HashSet<T> ). Nó chứa tất cả các phương thức thiết lập bắt buộc. IsSubsetOftrong trường hợp của bạn.


2

toán tử Linq SequenceEqual cũng sẽ hoạt động (nhưng nhạy cảm với các mục của liệt kê có cùng thứ tự)

return list1Uris.SequenceEqual(list2Uris);

2

Giải pháp được đánh dấu là câu trả lời sẽ không thành công trong trường hợp lặp lại. Nếu IEnumerable của bạn chỉ chứa các giá trị riêng biệt thì nó sẽ vượt qua.

Câu trả lời dưới đây dành cho 2 danh sách có sự lặp lại:

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

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

Đây không phải là một giải pháp tốt vì nó loại bỏ tất cả các bản sao và không thực sự so sánh chúng.
John

2

Bạn nên sử dụng HashSet thay vì Array.

Thí dụ:

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

Tài liệu tham khảo

Hạn chế duy nhất của HasSet là chúng ta không thể lấy mục theo chỉ mục như Danh sách cũng như không lấy mục theo Khóa như Từ điển. Tất cả những gì bạn có thể làm là liệt kê chúng (cho từng, trong khi, v.v.)

Vui lòng cho tôi biết nếu điều đó phù hợp với bạn


-2

bạn có thể sử dụng phương pháp này để so sánh hai danh sách

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

Khá nhiều câu trả lời tương tự đã được đưa ra 3 năm trước: stackoverflow.com/a/16967827/5282087
Dragomok
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.