programing

C# LINQ 목록에서 중복 항목 찾기

elseif 2023. 4. 29. 08:47

C# LINQ 목록에서 중복 항목 찾기

LINQ 사용, 에서List<int>두 번 이상 반복되는 항목과 해당 값을 포함하는 목록을 검색하려면 어떻게 해야 합니까?

문제를 해결하는 가장 쉬운 방법은 값을 기준으로 요소를 그룹화한 다음 그룹에 두 개 이상의 요소가 있는 경우 그룹의 대표자를 선택하는 것입니다.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());

요소를 키로 지정하고 반복되는 횟수를 값으로 지정하여 사전을 반환합니다.

열거형에 중복 항목이 있는지 확인합니다.

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

열거형의 모든 값이 고유한지 확인합니다.

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

다른 방법은 다음과 같습니다.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);
  }
}

중복 값만 찾으려면:

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

예.

var list = new[] {1,2,3,1,4,2};

GroupBy는 숫자를 키별로 그룹화하고 카운트(반복 횟수)를 유지합니다.그 후, 우리는 단지 한 번 이상 반복된 값을 확인하고 있습니다.

고유한 값만 찾는 방법

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

예.

var list = new[] {1,2,3,1,4,2};

GroupBy는 숫자를 키별로 그룹화하고 카운트(반복 횟수)를 유지합니다.그 후, 우리는 단지 한 번의 수단만 반복한 값들이 고유한지 확인하고 있습니다.

다음을 수행할 수 있습니다.

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()보다 빠릅니다.

이에 대응하기 위해 프로젝트에 포함할 수 있는 확장을 만들었습니다. 목록이나 링크에서 중복 항목을 검색할 때 가장 많은 경우가 반환됩니다.

예:

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

답이 있지만 왜 작동하지 않는지 이해할 수 없었습니다.

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

MS SQL Server에서 선택한 중복 함수의 전체 Link-to-SQL 확장 집합입니다.를 사용하지 않고 .ToList() 또는 IEnumberable입니다.이러한 쿼리는 메모리가 아닌 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()));
}

Linkq 쿼리:

var query = from s2 in (from s in someList group s by new { s.Column1, s.Column2 } into sg select sg) where s2.Count() > 1 select s2;

이것은 사용하지 않는 더 간단한 방법입니다. 그룹은 지역 요소를 가져온 다음 그것들을 반복하고 만약 그들의 수가 1보다 크면 목록에서 그들의 수를 확인합니다. 이것은 그것이 하나 이상의 항목으로 나타난다는 것을 의미하므로 반복 항목 목록에 그것을 추가합니다.

var mylist = new List<int>() { 1, 1, 2, 3, 3, 3, 4, 4, 4 };
            var distList=  mylist.Distinct().ToList();
            var Repeteditemlist = new List<int>();
            foreach (var item in distList)
            {
               if(mylist.Count(e => e == item) > 1)
                {
                    Repeteditemlist.Add(item);
                }
            }
            foreach (var item in Repeteditemlist)
            {
                Console.WriteLine(item);
            }

예상 출력:

1 3 4

다른 접근 방식일 뿐입니다.

HasDuplicate만 있는 경우:

bool hasAnyDuplicate = list.Count > list.Distinct().Count;

중복 값의 경우

List<string> duplicates = new List<string>();
duplicates.AddRange(list);
list.Distinct().ToList().ForEach(x => duplicates.Remove(x));

// for unique duplicate values:
duplicates.Distinct():

든모.GroupBy답은 가장 간단하지만 가장 효율적이지는 않을 것입니다.대용량 내부 컬렉션을 구축하면 할당 비용이 발생하기 때문에 메모리 성능에 특히 좋지 않습니다.

적절한 대안은 HuBeZa의 것입니다.HashSet.Add근거에 입각한 접근성능이 더 좋습니다.

Null에 관심이 없다면 CPU와 메모리 모두에서 이와 같은 것이 가장 효율적이라고 생각합니다.

public static IEnumerable<TProperty> Duplicates<TSource, TProperty>(
    this IEnumerable<TSource> source,
    Func<TSource, TProperty> duplicateSelector,
    IEqualityComparer<TProperty> comparer = null)
{
    comparer ??= EqualityComparer<TProperty>.Default;

    Dictionary<TProperty, int> counts = new Dictionary<TProperty, int>(comparer);

    foreach (var item in source)
    {
        TProperty property = duplicateSelector(item);
        counts.TryGetValue(property, out int count);

        switch (count)
        {
            case 0:
                counts[property] = ++count;
                break;

            case 1:
                counts[property] = ++count;
                yield return property;
                break;
        }
    }
}

여기서 요령은 중복 개수가 1에 도달하면 추가 조회 비용을 피하는 것입니다.각 항목에 대해 중복 발생 횟수를 원하는 경우 물론 사전을 카운트와 함께 계속 업데이트할 수 있습니다.null의 경우, 추가 처리가 필요합니다.

키로 중복 제거

myTupleList = myTupleList.GroupBy(tuple => tuple.Item1).Select(group => group.First()).ToList();

언급URL : https://stackoverflow.com/questions/18547354/c-sharp-linq-find-duplicates-in-list