ตรวจสอบว่า IEnumerable หนึ่งตัวมีองค์ประกอบทั้งหมดของ IEnumerable หรือไม่


103

วิธีใดเป็นวิธีที่เร็วที่สุดในการตรวจสอบว่า IEnumerable หนึ่งตัวมีองค์ประกอบทั้งหมดของ 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
รายการของคุณไปทางไหน? คุณต้องการตรวจสอบว่ารายการทั้งหมดใน list1 อยู่ในรายการ 2 หรือว่ารายการทั้งหมดใน list2 อยู่ในรายการ 1 หรือไม่
Mark Byers

คำตอบ:


140

ไม่มี "วิธีที่รวดเร็ว" ในการดำเนินการนี้เว้นแต่คุณจะติดตามและรักษาสถานะบางอย่างที่กำหนดว่าค่าทั้งหมดในคอลเล็กชันหนึ่งมีอยู่ในอีกชุดหนึ่งหรือไม่ ถ้าคุณต้องIEnumerable<T>ต่อสู้ฉันจะใช้Intersect.

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

ประสิทธิภาพของสิ่งนี้ควรมีความสมเหตุสมผลเนื่องจากIntersect()จะแจกแจงแต่ละรายการเพียงครั้งเดียว นอกจากนี้การเรียกครั้งที่สองCount()จะเหมาะสมที่สุดหากประเภทที่อยู่ภายใต้เป็นICollection<T>เพียงIEnumerable<T>ไฟล์.


ฉันทำการทดสอบบางอย่างและดูเหมือนว่าวิธีนี้จะทำงานได้เร็วกว่าวิธีอื่น ๆ ขอบคุณสำหรับทิป.
Brandon Zacharie

2
สิ่งนี้จะใช้ไม่ได้หากมีรายการที่ซ้ำกันในรายการ ตัวอย่างเช่นการเปรียบเทียบอาร์เรย์ char ของ 441 และ 414 จะส่งกลับ 41 และทำให้การนับล้มเหลว
John

69

คุณยังสามารถใช้ยกเว้นเพื่อลบออกจากรายการแรกค่าทั้งหมดที่มีอยู่ในรายการที่สองจากนั้นตรวจสอบว่ามีการลบค่าทั้งหมดหรือไม่:

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

วิธีนี้มีข้อดีคือไม่ต้องเรียกสองครั้งเพื่อ Count ()


นอกจากนี้ยังดีสำหรับการค้นหาว่ามีอะไรอยู่ใน List1 แต่ไม่อยู่ใน List2
โฮเมอร์

16
สิ่งนี้ใช้ได้ในสถานการณ์ที่ list1 มีค่าที่ซ้ำกัน คำตอบที่ยอมรับไม่ได้
dbc

23

C # 3.5+

ใช้Enumerable.All<TSource>เพื่อตรวจสอบว่ารายการ List2 ทั้งหมดมีอยู่ใน List1 หรือไม่:

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

สิ่งนี้จะใช้งานได้เมื่อ list1 มีมากกว่ารายการทั้งหมดของ list2


10
อุ๊ยที่ผลกระทบของการContains()โทรภายในการAll()โทร
Kent Boogaart

นอกจากนี้คุณสามารถย้ายไปยังวิธีการกลุ่ม: bool hasAll = list2Uris All (list1Uris.Contains);
jimpanzer

ในกรณีของ IEnumerable <T> ประเภทโซลูชันนี้จะให้ประสิทธิภาพ n * m
Dmitriy Dokshin

5
ชวเลข: bool hasAll = list2Uris.All(list1Uris.Contains);
Illuminator

3

คำตอบของ Kent นั้นดีและสั้น แต่วิธีแก้ปัญหาที่เขาให้มักจะต้องมีการทำซ้ำในคอลเลคชันแรกทั้งหมด นี่คือซอร์สโค้ด:

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

นั่นไม่จำเป็นเสมอไป ดังนั้นนี่คือทางออกของฉัน:

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

จริงๆแล้วคุณควรคิดถึงการใช้ISet<T>( HashSet<T>) มันมีวิธีการตั้งค่าที่จำเป็นทั้งหมด IsSubsetOfในกรณีของคุณ


2

ตัวดำเนินการ Linq SequenceEqual จะใช้งานได้เช่นกัน (แต่มีความไวต่อรายการที่นับได้ซึ่งอยู่ในลำดับเดียวกัน)

return list1Uris.SequenceEqual(list2Uris);

2

วิธีแก้ปัญหาที่ทำเครื่องหมายเป็นคำตอบจะล้มเหลวในกรณีที่เกิดซ้ำ หาก IEnumerable มีเฉพาะค่าที่แตกต่างกันก็จะผ่านไป

คำตอบด้านล่างมีไว้สำหรับ 2 รายการที่มีการทำซ้ำ:

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

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

นี่ไม่ใช่วิธีแก้ปัญหาที่ดีเนื่องจากจะลบรายการที่ซ้ำกันทั้งหมดออกและไม่ได้เปรียบเทียบจริงๆ
John

2

คุณควรใช้ HashSet แทน Array

ตัวอย่าง:

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

ข้อมูลอ้างอิง

ข้อ จำกัด ของ HasSet เพียงอย่างเดียวคือเราไม่สามารถรับไอเท็มจากดัชนีเช่นรายการหรือรับไอเท็มด้วยคีย์เช่นพจนานุกรม สิ่งที่คุณทำได้คือแจกแจง (สำหรับแต่ละคนในขณะที่ ฯลฯ )

โปรดแจ้งให้เราทราบหากวิธีนี้เหมาะกับคุณ


-2

คุณสามารถใช้วิธีนี้เพื่อเปรียบเทียบสองรายการ

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

คำตอบเดียวกันนี้ได้รับเมื่อ 3 ปีก่อน: stackoverflow.com/a/16967827/5282087
Dragomok
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.