C # LINQ tìm các bản sao trong Danh sách


333

Sử dụng LINQ, từ a List<int>, làm cách nào tôi có thể truy xuất danh sách chứa các mục được lặp lại nhiều lần và các giá trị của chúng?

Câu trả lời:


566

Cách dễ nhất để giải quyết vấn đề là nhóm các yếu tố dựa trên giá trị của chúng, sau đó chọn một đại diện của nhóm nếu có nhiều hơn một yếu tố trong nhóm. Trong LINQ, điều này dịch là:

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

Nếu bạn muốn biết các yếu tố được lặp lại bao nhiêu lần, bạn có thể sử dụng:

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

Điều này sẽ trả về một Listloại ẩn danh và mỗi phần tử sẽ có các thuộc tính ElementCounter, để lấy thông tin bạn cần.

Và cuối cùng, nếu đó là một cuốn từ điển bạn đang tìm kiếm, bạn có thể sử dụng

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

Điều này sẽ trả về một từ điển, với phần tử của bạn là khóa và số lần nó được lặp lại dưới dạng giá trị.


Bây giờ chỉ là một thắc mắc, giả sử rằng int trùng lặp được phân phối thành n mảng int, tôi sử dụng từ điển và vòng lặp để hiểu mảng nào chứa một bản sao và loại bỏ nó theo logic phân phối, có cách nhanh nhất (linq tự hỏi) để đạt được kết quả đó? cảm ơn bạn trước sự quan tâm
Mirko Arcese

Tôi đang làm một cái gì đó như thế này: code for (int i = 0; i <trùng lặp.Count; i ++) {int trùng lặp = trùng lặp [i]; trùng lặpLocation.Add (trùng lặp, Danh sách mới <int> ()); for (int k = 0; k <hitList.Ldrops; k ++) {if (hitList [k] .Contains (trùng lặp)) {trùng lặpLocation.EuityAt (i) .Value.Add (k); }} // loại bỏ trùng lặp theo một số quy tắc. }code
Mirko Arcese

nếu bạn muốn tìm các bản sao trong danh sách các mảng, hãy xem ChọnMany
Lưu

Tôi đang tìm kiếm các bản sao trong một loạt các danh sách, nhưng không hiểu làm thế nào nhiều người có thể giúp tôi tìm ra nó
Mirko Arcese

1
Để kiểm tra xem bất kỳ bộ sưu tập nào có nhiều hơn một phần tử hay không nếu sử dụng Skip (1) .Any () thay vì Count () hiệu quả hơn. Hãy tưởng tượng một bộ sưu tập với 1000 yếu tố. Bỏ qua (1) .Any () sẽ phát hiện có nhiều hơn 1 khi tìm thấy phần tử thứ 2. Sử dụng Count () yêu cầu truy cập bộ sưu tập hoàn chỉnh.
Harald Coppoolse

133

Tìm hiểu xem một liệt kê có chứa bất kỳ bản sao :

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

Tìm hiểu xem tất cả các giá trị trong một liệt kê là duy nhất :

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

Có khả năng nào không phải lúc nào cũng là đối lập boolean? anyD repeatate ==! allUnique trong mọi trường hợp.
Garr Godfrey

1
@GarrGodfrey Họ luôn đối nghịch nhau
Caltor

21

Một cách khác là sử dụng HashSet:

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

Nếu bạn muốn các giá trị duy nhất trong danh sách trùng lặp của bạn:

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();

Đây là giải pháp tương tự như một phương pháp mở rộng chung:

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

Điều này không hoạt động như mong đợi. Sử dụng List<int> { 1, 2, 3, 4, 5, 2 }làm nguồn, kết quả là một IEnumerable<int>phần tử có giá trị là 1(trong đó giá trị trùng lặp chính xác là 2)
BCA

@BCA ngày hôm qua, tôi nghĩ bạn đã sai. Kiểm tra ví dụ này: dotnetfiddle.net/GUnhUl
HuBeZa

Fiddle của bạn in ra kết quả chính xác. Tuy nhiên, tôi đã thêm dòng Console.WriteLine("Count: {0}", duplicates.Count());trực tiếp bên dưới nó và nó in 6. Trừ khi tôi thiếu một cái gì đó về các yêu cầu cho chức năng này, chỉ nên có 1 mục trong bộ sưu tập kết quả.
BCA

@BCA ngày hôm qua, đó là một lỗi do LINQ trì hoãn thực thi. Tôi đã thêm ToListvào để khắc phục sự cố, nhưng điều đó có nghĩa là phương thức được thực thi ngay khi nó được gọi, chứ không phải khi bạn lặp lại kết quả.
HuBeZa

var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));sẽ dẫn đến một danh sách bao gồm tất cả các lần xuất hiện trùng lặp. Vì vậy, nếu bạn có bốn lần xuất hiện của 2 trong danh sách của mình, thì danh sách trùng lặp của bạn sẽ chứa ba lần xuất hiện của 2, vì chỉ một trong hai lần xuất hiện có thể được thêm vào Hashset. Nếu bạn muốn danh sách của mình chứa các giá trị duy nhất cho mỗi bản sao, thay vào đó hãy sử dụng mã này:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy

10

Bạn có thể làm được việc này:

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

Với các phương thức mở rộng này:

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

Sử dụng IsMult Môn () trong phương thức Sao chép nhanh hơn Count () vì điều này không lặp lại toàn bộ bộ sưu tập.


Nếu bạn nhìn vào nguồn tham chiếu cho Nhóm, bạn có thể thấy đó Count() tính toán trước và giải pháp của bạn có thể chậm hơn.
Johnbot

@ John John. Bạn đã đúng, trong trường hợp này là nhanh hơn và việc triển khai có thể sẽ không bao giờ thay đổi ... nhưng nó phụ thuộc vào một chi tiết triển khai của lớp ẩn sau đằng sau IGrouping. Với triển khai của tôi, bạn biết nó sẽ không bao giờ lặp lại toàn bộ bộ sưu tập.
Alex Siepman

vì vậy việc đếm [ Count()] về cơ bản khác với việc lặp lại toàn bộ danh sách. Count()được tính toán trước nhưng lặp lại toàn bộ danh sách thì không.
Jogi

@rehan khan: Tôi không hiểu sự khác biệt giữa Count () và Count ()
Alex Siepman

2
@RehanKhan: IsMultipl KHÔNG thực hiện Count (), nó dừng ngay lập tức sau 2 mục. Cũng giống như Take (2) .Count> = 2;
Alex Siepman

6

Tôi đã tạo ra một sự mở rộng để đáp ứng với điều này, bạn có thể đưa nó vào các dự án của bạn, tôi nghĩ điều này sẽ trả lại nhiều trường hợp nhất khi bạn tìm kiếm các bản sao trong Danh sách hoặc Linq.

Thí dụ:

//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
Cân nhắc sử dụng Skip (1) .Any () thay vì Count (). Nếu bạn có 1000 bản sao, thì Skip (1) .Any () sẽ dừng sau khi tìm thấy bản sao thứ hai. Count () sẽ truy cập tất cả 1000 phần tử.
Harald Coppoolse

1
Nếu bạn thêm phương thức tiện ích mở rộng này, hãy xem xét sử dụng Hashset.Add thay vì GroupBy, như được đặt trong một trong những câu trả lời khác. Ngay khi HashSet.Add tìm thấy một bản sao, nó sẽ dừng lại. GroupBy của bạn sẽ tiếp tục nhóm tất cả các yếu tố, ngay cả khi một nhóm có nhiều hơn một yếu tố đã được tìm thấy
Harald Coppoolse

6

Để chỉ tìm các giá trị trùng lặp:

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

Ví dụ. danh sách var = new [] {1,2,3,1,4,2};

vì vậy nhóm theo sẽ nhóm các số theo các khóa của chúng và sẽ duy trì số đếm (số lần lặp lại) với nó. Sau đó, chúng tôi chỉ kiểm tra các giá trị đã lặp lại nhiều lần.

Để tìm các giá trị uniuqe chỉ:

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

Ví dụ. danh sách var = new [] {1,2,3,1,4,2};

vì vậy nhóm theo sẽ nhóm các số theo các khóa của chúng và sẽ duy trì số đếm (số lần lặp lại) với nó. Sau đó, chúng tôi chỉ kiểm tra các giá trị đã lặp lại một lần có nghĩa là duy nhất.


Mã bên dưới cũng sẽ tìm thấy các mặt hàng độc đáo. var unique = list.Distinct(x => x)
Malu MN

1

Toàn bộ bộ phần mở rộng Linq to SQL của các chức năng trùng lặp được kiểm tra trong MS SQL Server. Không cần sử dụng .ToList () hoặc IEnumerable. Các truy vấn này thực thi trong SQL Server chứ không phải trong bộ nhớ. . Kết quả chỉ trả về tại bộ nhớ.

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

Có một câu trả lời nhưng tôi không hiểu tại sao nó không hoạt động;

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

giải pháp của tôi là như thế trong tình huống này;

var duplicates = model.list
                    .GroupBy(s => s.SAME_ID)
                    .Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
    doSomething();
}
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.