C # LINQ ค้นหารายการที่ซ้ำกันในรายการ


333

การใช้ LINQ จากList<int>ฉันจะเรียกรายการที่มีรายการซ้ำกันมากกว่าหนึ่งครั้งและค่าของพวกเขาได้อย่างไร

คำตอบ:


567

วิธีที่ง่ายที่สุดในการแก้ปัญหาคือการจัดกลุ่มองค์ประกอบตามค่าของพวกเขาแล้วเลือกตัวแทนของกลุ่มหากมีองค์ประกอบมากกว่าหนึ่งองค์ประกอบในกลุ่ม ใน LINQ สิ่งนี้แปลเป็น:

var query = lst.GroupBy(x => x)
              .Where(g => g.Count() > 1)
              .Select(y => y.Key)
              .ToList();

หากคุณต้องการทราบจำนวนองค์ประกอบที่ซ้ำกันคุณสามารถใช้:

var query = lst.GroupBy(x => x)
              .Where(g => g.Count() > 1)
              .Select(y => new { Element = y.Key, Counter = y.Count() })
              .ToList();

สิ่งนี้จะส่งคืนListชนิดที่ไม่ระบุชื่อและแต่ละองค์ประกอบจะมีคุณสมบัติElementและCounterเพื่อดึงข้อมูลที่คุณต้องการ

และสุดท้ายถ้าเป็นพจนานุกรมที่คุณต้องการคุณสามารถใช้

var query = lst.GroupBy(x => x)
              .Where(g => g.Count() > 1)
              .ToDictionary(x => x.Key, y => y.Count());

สิ่งนี้จะส่งคืนพจนานุกรมโดยที่องค์ประกอบของคุณเป็นกุญแจและจำนวนครั้งที่มีการทำซ้ำเป็นค่า


ทีนี้ก็เป็นเรื่องน่าประหลาดใจสมมติว่า int ที่ซ้ำกันจะถูกกระจายไปยัง n int arrays ผมใช้พจนานุกรมและสำหรับลูปที่จะเข้าใจว่าอาเรย์ใดที่มีซ้ำและลบออกตามตรรกะของการกระจายมีวิธีที่เร็วที่สุด (linq สงสัย) บรรลุผลนั้น? ขอบคุณล่วงหน้าสำหรับความสนใจ
Mirko Arcese

ฉันกำลังทำสิ่งนี้: code สำหรับ (int i = 0; i <replates.Count; i ++) {int ซ้ำ = ซ้ำกัน [i]; ซ้ำซ้อนตำแหน่งเพิ่ม (ซ้ำรายการใหม่ <int> ()); สำหรับ (int k = 0; k <hitsList.Length; k ++) {ถ้า (hitsList [k] .Contains (ซ้ำ)) {ซ้ำatesLocation.ElementAt (i) .Value.Add (k); }} // ลบรายการที่ซ้ำกันตามกฎบางอย่าง }code
Mirko Arcese

ถ้าคุณต้องการค้นหารายการที่ซ้ำกันในรายการของอาร์เรย์ให้ดูที่ SelectMany
บันทึก

ฉันค้นหารายการที่ซ้ำกันในรายการต่าง ๆ แต่ไม่ได้รับว่า selectmany สามารถช่วยฉันทำมันออกมาได้อย่างไร
Mirko Arcese

1
ในการตรวจสอบว่าคอลเลกชันใด ๆ มีมากกว่าหนึ่งองค์ประกอบถ้ามีประสิทธิภาพมากกว่าในการใช้ข้าม (1) .Any () แทน Count () ลองนึกภาพคอลเลกชันที่มีองค์ประกอบ 1,000 รายการ ข้าม (1) .Any () จะตรวจพบว่ามีมากกว่า 1 เมื่อพบองค์ประกอบที่ 2 การใช้ Count () จำเป็นต้องเข้าถึงการรวบรวมทั้งหมด
Harald Coppoolse

133

ค้นหาว่าสิ่งที่นับได้มีซ้ำกันหรือไม่ :

var anyDuplicate = enumerable.GroupBy(x => x.Key).Any(g => g.Count() > 1);

ค้นหาว่าค่าทั้งหมดในการแจกแจงเป็นค่าเฉพาะหรือไม่ :

var allUnique = enumerable.GroupBy(x => x.Key).All(g => g.Count() == 1);

มีความเป็นไปได้หรือไม่ที่สิ่งเหล่านี้จะไม่ตรงข้ามแบบบูลเสมอ anyDuplicate ==! allUnique ในทุกกรณี
Garr Godfrey

1
@GarrGodfrey พวกเขามักจะตรงข้ามแบบบูล
Caltor

21

วิธีอื่นใช้HashSet:

var hash = new HashSet<int>();
var duplicates = list.Where(i => !hash.Add(i));

หากคุณต้องการค่าที่ไม่ซ้ำในรายการที่ซ้ำกันของคุณ:

var myhash = new HashSet<int>();
var mylist = new List<int>(){1,1,2,2,3,3,3,4,4,4};
var duplicates = mylist.Where(item => !myhash.Add(item)).Distinct().ToList();

นี่คือโซลูชันเดียวกับวิธีการขยายทั่วไป:

public static class Extensions
{
  public static IEnumerable<TSource> GetDuplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IEqualityComparer<TKey> comparer)
  {
    var hash = new HashSet<TKey>(comparer);
    return source.Where(item => !hash.Add(selector(item))).ToList();
  }

  public static IEnumerable<TSource> GetDuplicates<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
  {
    return source.GetDuplicates(x => x, comparer);      
  }

  public static IEnumerable<TSource> GetDuplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
  {
    return source.GetDuplicates(selector, null);
  }

  public static IEnumerable<TSource> GetDuplicates<TSource>(this IEnumerable<TSource> source)
  {
    return source.GetDuplicates(x => x, null);
  }
}

สิ่งนี้ไม่ทำงานตามที่คาดไว้ การใช้List<int> { 1, 2, 3, 4, 5, 2 }เป็นแหล่งที่มาผลลัพธ์จะเป็นIEnumerable<int>องค์ประกอบหนึ่งที่มีค่า1(ซึ่งค่าที่ซ้ำกันที่ถูกต้องคือ 2)
BCA

@BCA เมื่อวานนี้ฉันคิดว่าคุณผิด ตรวจสอบตัวอย่างนี้dotnetfiddle.net/GUnhUl
HuBeZa

ซอของคุณพิมพ์ผลลัพธ์ที่ถูกต้องออกมา อย่างไรก็ตามฉันเพิ่มบรรทัดConsole.WriteLine("Count: {0}", duplicates.Count());ด้านล่างลงไปและพิมพ์6ออกมา ยกเว้นว่าฉันขาดอะไรบางอย่างเกี่ยวกับข้อกำหนดของฟังก์ชันนี้ควรมีเพียง 1 รายการในการรวบรวมผลลัพธ์
BCA

@BCA เมื่อวานนี้เป็นข้อผิดพลาดที่เกิดจากการดำเนินการรอการตัดบัญชี LINQ ฉันได้เพิ่มToListเพื่อแก้ไขปัญหา แต่หมายความว่าวิธีการจะดำเนินการทันทีที่เรียกว่าและไม่ใช่เมื่อคุณทำซ้ำมากกว่าผลลัพธ์
HuBeZa

var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));จะนำไปสู่รายการที่มีรายการซ้ำทั้งหมด ดังนั้นหากคุณมีการเกิดขึ้นสองครั้งที่สองในรายการของคุณรายการที่ซ้ำกันของคุณจะมีการเกิดขึ้นสองครั้งที่ 2 เนื่องจากมีเพียงหนึ่งในสองรายการที่สามารถเพิ่มลงใน HashSet หากคุณต้องการให้รายการของคุณมีค่าที่ไม่ซ้ำกันสำหรับแต่ละรายการที่ซ้ำกันให้ใช้รหัสนี้แทน:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy

10

คุณสามารถทำได้:

var list = new[] {1,2,3,1,4,2};
var duplicateItems = list.Duplicates();

ด้วยวิธีการต่อไปนี้:

public static class Extensions
{
    public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
    {
        var grouped = source.GroupBy(selector);
        var moreThan1 = grouped.Where(i => i.IsMultiple());
        return moreThan1.SelectMany(i => i);
    }

    public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source)
    {
        return source.Duplicates(i => i);
    }

    public static bool IsMultiple<T>(this IEnumerable<T> source)
    {
        var enumerator = source.GetEnumerator();
        return enumerator.MoveNext() && enumerator.MoveNext();
    }
}

การใช้ IsMultiple () ในวิธีการทำซ้ำเร็วกว่า Count () เนื่องจากจะไม่ทำซ้ำการรวบรวมทั้งหมด


หากคุณดูที่แหล่งอ้างอิงสำหรับการจัดกลุ่มคุณจะเห็นว่าCount() มีการคำนวณไว้ล่วงหน้าและโซลูชันของคุณน่าจะช้ากว่า
Johnbot

@Johnbot คุณถูกต้องในกรณีนี้มันเร็วกว่าและการใช้งานจะไม่เปลี่ยนแปลง ... แต่ขึ้นอยู่กับรายละเอียดการใช้งานของคลาสที่ไม่สมบูรณ์หลังการจัดกลุ่ม IGrouping ด้วย implementaion ของฉันคุณรู้ว่ามันจะไม่ซ้ำคอลเลกชันทั้งหมด
Alex Siepman

ดังนั้นการนับ [ Count()] จึงแตกต่างจากการวนซ้ำรายการทั้งหมด Count()ถูกคำนวณล่วงหน้า แต่วนซ้ำรายการทั้งหมดไม่ได้
Jogi

@rehan khan: ฉันไม่เข้าใจความแตกต่างระหว่าง Count () และ Count ()
Alex Siepman เมื่อ

2
@RehanKhan: IsMultiple ไม่ทำการนับ () จะหยุดทันทีหลังจาก 2 รายการ เช่นเดียวกับ Take (2) .Count> = 2;
Alex Siepman

6

ฉันสร้างส่วนขยายเพื่อตอบสนองต่อสิ่งนี้คุณสามารถรวมไว้ในโครงการของคุณได้ฉันคิดว่านี่จะเป็นกรณีที่พบมากที่สุดเมื่อคุณค้นหารายการที่ซ้ำกันในรายการหรือ Linq

ตัวอย่าง:

//Dummy class to compare in list
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public Person(int id, string name, string surname)
    {
        this.Id = id;
        this.Name = name;
        this.Surname = surname;
    }
}


//The extention static class
public static class Extention
{
    public static IEnumerable<T> getMoreThanOnceRepeated<T>(this IEnumerable<T> extList, Func<T, object> groupProps) where T : class
    { //Return only the second and next reptition
        return extList
            .GroupBy(groupProps)
            .SelectMany(z => z.Skip(1)); //Skip the first occur and return all the others that repeats
    }
    public static IEnumerable<T> getAllRepeated<T>(this IEnumerable<T> extList, Func<T, object> groupProps) where T : class
    {
        //Get All the lines that has repeating
        return extList
            .GroupBy(groupProps)
            .Where(z => z.Count() > 1) //Filter only the distinct one
            .SelectMany(z => z);//All in where has to be retuned
    }
}

//how to use it:
void DuplicateExample()
{
    //Populate List
    List<Person> PersonsLst = new List<Person>(){
    new Person(1,"Ricardo","Figueiredo"), //fist Duplicate to the example
    new Person(2,"Ana","Figueiredo"),
    new Person(3,"Ricardo","Figueiredo"),//second Duplicate to the example
    new Person(4,"Margarida","Figueiredo"),
    new Person(5,"Ricardo","Figueiredo")//third Duplicate to the example
    };

    Console.WriteLine("All:");
    PersonsLst.ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
    /* OUTPUT:
        All:
        1 -> Ricardo Figueiredo
        2 -> Ana Figueiredo
        3 -> Ricardo Figueiredo
        4 -> Margarida Figueiredo
        5 -> Ricardo Figueiredo
        */

    Console.WriteLine("All lines with repeated data");
    PersonsLst.getAllRepeated(z => new { z.Name, z.Surname })
        .ToList()
        .ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
    /* OUTPUT:
        All lines with repeated data
        1 -> Ricardo Figueiredo
        3 -> Ricardo Figueiredo
        5 -> Ricardo Figueiredo
        */
    Console.WriteLine("Only Repeated more than once");
    PersonsLst.getMoreThanOnceRepeated(z => new { z.Name, z.Surname })
        .ToList()
        .ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
    /* OUTPUT:
        Only Repeated more than once
        3 -> Ricardo Figueiredo
        5 -> Ricardo Figueiredo
        */
}

1
พิจารณาใช้การข้าม (1) .Any () แทน Count () หากคุณมี 1,000 รายการซ้ำข้าม (1) .Any () จะหยุดทำงานหลังจากพบรายการที่สอง จำนวน () จะเข้าถึงองค์ประกอบทั้งหมด 1,000 รายการ
Harald Coppoolse

1
หากคุณเพิ่มวิธีการขยายนี้ให้พิจารณาใช้ HashSet.Add แทน GroupBy ตามที่ได้รับในคำตอบอื่น ทันทีที่ HashSet.Add พบสำเนาที่ซ้ำกันมันจะหยุดทำงาน GroupBy ของคุณจะทำการจัดกลุ่มองค์ประกอบทั้งหมดต่อไปแม้ว่าจะพบกลุ่มที่มีองค์ประกอบมากกว่าหนึ่งรายการ
Harald Coppoolse

6

ในการค้นหาค่าที่ซ้ำกันเท่านั้น:

var duplicates = list.GroupBy(x => x.Key).Any(g => g.Count() > 1);

เช่น. รายการ var = new [] {1,2,3,1,4,2};

ดังนั้นจัดกลุ่มตามจะจัดกลุ่มตัวเลขด้วยปุ่มของพวกเขาและจะรักษาจำนวน (จำนวนครั้งที่ทำซ้ำ) ไว้ด้วย หลังจากนั้นเราแค่ตรวจสอบค่าที่ทำซ้ำมากกว่าหนึ่งครั้ง

ในการค้นหาค่า uniuqe เท่านั้น:

var unique = list.GroupBy(x => x.Key).All(g => g.Count() == 1);

เช่น. รายการ var = new [] {1,2,3,1,4,2};

ดังนั้นจัดกลุ่มตามจะจัดกลุ่มตัวเลขด้วยปุ่มของพวกเขาและจะรักษาจำนวน (จำนวนครั้งที่ทำซ้ำ) ไว้ด้วย หลังจากนั้นเราเพียงตรวจสอบค่าที่ทำซ้ำเพียงครั้งเดียวหมายความว่าไม่ซ้ำกัน


รหัสด้านล่างจะค้นหารายการที่ไม่ซ้ำ var unique = list.Distinct(x => x)
Malu MN

1

ชุด Linq ไปเป็นส่วนขยาย SQL ของฟังก์ชั่นที่ซ้ำซ้อนที่ตรวจสอบใน MS SQL Server โดยไม่ใช้. ToList () หรือ IEnumerable แบบสอบถามเหล่านี้ดำเนินการใน SQL Server มากกว่าในหน่วยความจำ . ผลลัพธ์จะกลับมาที่หน่วยความจำเท่านั้น

public static class Linq2SqlExtensions {

    public class CountOfT<T> {
        public T Key { get; set; }
        public int Count { get; set; }
    }

    public static IQueryable<TKey> Duplicates<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
        => source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(s => s.Key);

    public static IQueryable<TSource> GetDuplicates<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
        => source.GroupBy(groupBy).Where(w => w.Count() > 1).SelectMany(s => s);

    public static IQueryable<CountOfT<TKey>> DuplicatesCounts<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
        => source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(y => new CountOfT<TKey> { Key = y.Key, Count = y.Count() });

    public static IQueryable<Tuple<TKey, int>> DuplicatesCountsAsTuble<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
        => source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(s => Tuple.Create(s.Key, s.Count()));
}

0

มีคำตอบ แต่ฉันไม่เข้าใจว่าทำไมไม่ทำงาน

var anyDuplicate = enumerable.GroupBy(x => x.Key).Any(g => g.Count() > 1);

ทางออกของฉันเป็นเช่นนั้นในสถานการณ์นี้;

var duplicates = model.list
                    .GroupBy(s => s.SAME_ID)
                    .Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
    doSomething();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.