source

C#의 목록에서 중복 항목을 제거합니다.

manysource 2023. 4. 25. 22:36

C#의 목록에서 중복 항목을 제거합니다.

C#의 일반 목록을 빠르게 복제할 수 있는 방법이 있습니까?

사용 중인 경우입니다.넷 3+, Linq를 사용할 수 있습니다.

List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();

HashSet 사용을 고려해 보는 것이 좋습니다.

MSDN 링크에서 다음을 수행합니다.

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        HashSet<int> evenNumbers = new HashSet<int>();
        HashSet<int> oddNumbers = new HashSet<int>();

        for (int i = 0; i < 5; i++)
        {
            // Populate numbers with just even numbers.
            evenNumbers.Add(i * 2);

            // Populate oddNumbers with just odd numbers.
            oddNumbers.Add((i * 2) + 1);
        }

        Console.Write("evenNumbers contains {0} elements: ", evenNumbers.Count);
        DisplaySet(evenNumbers);

        Console.Write("oddNumbers contains {0} elements: ", oddNumbers.Count);
        DisplaySet(oddNumbers);

        // Create a new HashSet populated with even numbers.
        HashSet<int> numbers = new HashSet<int>(evenNumbers);
        Console.WriteLine("numbers UnionWith oddNumbers...");
        numbers.UnionWith(oddNumbers);

        Console.Write("numbers contains {0} elements: ", numbers.Count);
        DisplaySet(numbers);
    }

    private static void DisplaySet(HashSet<int> set)
    {
        Console.Write("{");
        foreach (int i in set)
        {
            Console.Write(" {0}", i);
        }
        Console.WriteLine(" }");
    }
}

/* This example produces output similar to the following:
 * evenNumbers contains 5 elements: { 0 2 4 6 8 }
 * oddNumbers contains 5 elements: { 1 3 5 7 9 }
 * numbers UnionWith oddNumbers...
 * numbers contains 10 elements: { 0 2 4 6 8 1 3 5 7 9 }
 */

다음은 어떻습니까?

var noDupes = list.Distinct().ToList();

.net 3.5에서요?

동일한 유형의 목록을 사용하여 HashSet을 초기화하기만 하면 됩니다.

var noDupes = new HashSet<T>(withDupes);

또는 목록을 반환하려면 다음을 수행하십시오.

var noDupsList = new HashSet<T>(withDupes).ToList();

정렬한 다음 중복 항목이 서로 뭉치게 되므로 옆에 있는 두 개 및 두 개를 확인합니다.

이런 식입니다.

list.Sort();
Int32 index = list.Count - 1;
while (index > 0)
{
    if (list[index] == list[index - 1])
    {
        if (index < list.Count - 1)
            (list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]);
        list.RemoveAt(list.Count - 1);
        index--;
    }
    else
        index--;
}

주의:

  • 탈거 후 목록을 다시 작성할 필요가 없도록 뒤쪽에서 앞쪽을 비교합니다.
  • 이 예에서는 이제 C# Value Tuples를 사용하여 스왑을 수행하고, 이를 사용할 수 없는 경우 적절한 코드로 대체합니다.
  • 최종 결과가 더 이상 정렬되지 않습니다.

다음 명령을 사용합니다.

List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
                                                 .GroupBy(s => s.City)
                                                 .Select(grp => grp.FirstOrDefault())
                                                 .OrderBy(s => s.City)
                                                 .ToList();

목록에 다음 필드가 있습니다.ID, StoreName, City, PostalCode 값이 중복된 드롭다운에 도시 목록을 표시하고자 했습니다. 해결 방법: 도시별로 그룹화한 다음 목록의 첫 번째 도시를 선택합니다.

나한테는 효과가 있었어요. 간단히 사용하세요.

List<Type> liIDs = liIDs.Distinct().ToList<Type>();

"Type"을 원하는 유형(예: int)으로 바꿉니다.

네, 그렇습니다. 3.5를 사용할 수 있습니다.Distinct()요.

.Net 2에서는 다음을 모방할 수 있습니다.

public IEnumerable<T> DedupCollection<T> (IEnumerable<T> input) 
{
    var passedValues = new HashSet<T>();

    // Relatively simple dupe check alg used as example
    foreach(T item in input)
        if(passedValues.Add(item)) // True if item is new
            yield return item;
}

수집을 중복 제거하는 데 사용할 수 있으며 원래 순서로 값을 반환합니다.

일반적으로 수집을 필터링하는 것이 빠릅니다(둘 다 그렇듯이).Distinct()그리고 이 샘플은 항목 제거보다 더 효과적입니다.

연장 방법이 괜찮은 방법일 수도 있습니다...다음과 같습니다.

public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
    return listToDeduplicate.Distinct().ToList();
}

그런 다음 다음과 같이 전화하십시오.

List<int> myFilteredList = unfilteredList.Deduplicate();

Java의 경우(C#은 거의 동일할 것으로 예상됩니다)

list = new ArrayList<T>(new HashSet<T>(list))

원래 목록을 변환하려면 다음을 수행합니다.

List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);

순서를 유지하려면 HashSet을 LinkedHashSet으로 바꾸면 됩니다.

이렇게 하면 (요소가 중복되지 않는) 요소가 구분되어 다시 목록으로 변환됩니다.

List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();

Linq의 유니언 방법을 사용합니다.

참고: 이 솔루션에는 Linq에 대한 지식이 필요하지 않습니다.

코드입니다.

먼저 클래스 파일의 맨 위에 다음을 추가합니다.

using System.Linq;

자, 이제 다음 내용을 사용해서 중복되는 것을 제거할 수 있습니다.obj1다음을 참조하십시오

obj1 = obj1.Union(obj1).ToList();

참고 이름을 note: 름름바바바바바바이다로 바꿉니다.obj1개체 이름으로 이동합니다.

어떻게 작동합니까?

  1. Union 명령은 두 소스 개체의 각 항목 중 하나를 나열합니다.obj1은 둘 다 소스 개체이므로 obj1을 각 항목 중 하나로 줄입니다.

  2. »는 다음과 같습니다.ToList()이겁니다.이것은 린크가 명령어를 하기 때문에 필요합니다. 왜냐하면 Linq는 다음과 같은 명령을 하기 때문입니다.Union원래 목록을 수정하거나 새 목록을 반환하는 대신 IEnumerable 결과로 결과를 반환합니다.

도우미 방법(Linq 없음)으로 다음을 수행합니다.

public static List<T> Distinct<T>(this List<T> list)
{
    return (new HashSet<T>(list)).ToList();
}

인접한 중복 항목을 현장에서 제거하는 확장 방법이 있습니다.먼저 Sort()를 호출하고 동일한 IComparer를 전달합니다.이는 RemoveAt를 반복적으로 호출하는 Lasse V. Karlsen 버전보다 더 효율적일 것입니다(여러 블록 메모리 이동이 발생).

public static void RemoveAdjacentDuplicates<T>(this List<T> List, IComparer<T> Comparer)
{
    int NumUnique = 0;
    for (int i = 0; i < List.Count; i++)
        if ((i == 0) || (Comparer.Compare(List[NumUnique - 1], List[i]) != 0))
            List[NumUnique++] = List[i];
    List.RemoveRange(NumUnique, List.Count - NumUnique);
}

Nuget을 통해 MoreLINQ 패키지를 설치하면 속성별로 객체 목록을 쉽게 구분할 수 있습니다.

IEnumerable<Catalogue> distinctCatalogues = catalogues.DistinctBy(c => c.CatalogueCode); 

예인 클래스가 있는 경우입니다.Product그리고요.Customer중복된 항목을 목록에서 삭제하려고 합니다.

public class Product
{
    public int Id { get; set; }
    public string ProductName { get; set; }
}

public class Customer
{
    public int Id { get; set; }
    public string CustomerName { get; set; }

}

아래 양식에 일반 클래스를 정의해야 합니다.

public class ItemEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    private readonly PropertyInfo _propertyInfo;

    public ItemEqualityComparer(string keyItem)
    {
        _propertyInfo = typeof(T).GetProperty(keyItem, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
    }

    public bool Equals(T x, T y)
    {
        var xValue = _propertyInfo?.GetValue(x, null);
        var yValue = _propertyInfo?.GetValue(y, null);
        return xValue != null && yValue != null && xValue.Equals(yValue);
    }

    public int GetHashCode(T obj)
    {
        var propertyValue = _propertyInfo.GetValue(obj, null);
        return propertyValue == null ? 0 : propertyValue.GetHashCode();
    }
}

그런 다음 목록에서 중복 항목을 제거할 수 있습니다.

var products = new List<Product>
            {
                new Product{ProductName = "product 1" ,Id = 1,},
                new Product{ProductName = "product 2" ,Id = 2,},
                new Product{ProductName = "product 2" ,Id = 4,},
                new Product{ProductName = "product 2" ,Id = 4,},
            };
var productList = products.Distinct(new ItemEqualityComparer<Product>(nameof(Product.Id))).ToList();

var customers = new List<Customer>
            {
                new Customer{CustomerName = "Customer 1" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
            };
var customerList = customers.Distinct(new ItemEqualityComparer<Customer>(nameof(Customer.Id))).ToList();

이 코드는 중복된 항목을 제거합니다.Id다른 속성으로 중복된 항목을 제거하고 경우 변경할 수 있습니다.nameof(YourClass.DuplicateProperty)똑같아요.nameof(Customer.CustomerName)그런 다음 중복된 항목을 제거하세요.CustomerName소유물.

순서를 신경 쓰지 않으시면 그냥 물건에 넣으셔도 됩니다.HashSet순서를 유지 관리하려면 다음과 같은 작업을 수행할 수 있습니다.

var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
    if (hs.Add(t))
        unique.Add(t);

또는 Linq 방식입니다.

var hs = new HashSet<T>();
list.All( x =>  hs.Add(x) );

편집: The(편집:HashSet방법은 다음과 같습니다.O(N)시간 및 시 time time time time time time timeO(N)(@lassevk 및 기타에서 제안하는 바와 같이) 정렬한 후 고유하게 만드는 동안 공간은 다음과 같습니다.O(N*lgN)시간 및 시 time time time time time time timeO(1)(얼핏 본 것처럼) 하다는 것을 알 수 없습니다.

중복 항목이 목록에 추가되지 않도록 확인하는 것이 더 쉬울 수 있습니다.

if(items.IndexOf(new_item) < 0) 
    items.add(new_item)

유니언을 사용할 수 있습니다.

obj2 = obj1.Union(obj1).ToList();

다른 방법으로요.네트워크 2.0입니다.

    static void Main(string[] args)
    {
        List<string> alpha = new List<string>();

        for(char a = 'a'; a <= 'd'; a++)
        {
            alpha.Add(a.ToString());
            alpha.Add(a.ToString());
        }

        Console.WriteLine("Data :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t); });

        alpha.ForEach(delegate (string v)
                          {
                              if (alpha.FindAll(delegate(string t) { return t == v; }).Count > 1)
                                  alpha.Remove(v);
                          });

        Console.WriteLine("Unique Result :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t);});
        Console.ReadKey();
    }

여러 가지 해결 방법이 있습니다. 아래 목록의 중복 문제는 다음 중 하나입니다.

List<Container> containerList = LoadContainer();//Assume it has duplicates
List<Container> filteredList = new  List<Container>();
foreach (var container in containerList)
{ 
  Container duplicateContainer = containerList.Find(delegate(Container checkContainer)
  { return (checkContainer.UniqueId == container.UniqueId); });
   //Assume 'UniqueId' is the property of the Container class on which u r making a search

    if(!containerList.Contains(duplicateContainer) //Add object when not found in the new class object
      {
        filteredList.Add(container);
       }
  }

건배 라비 가네산

읽기 어려운 LINQ나 목록을 미리 정렬할 필요가 없는 간단한 솔루션이 있습니다.

   private static void CheckForDuplicateItems(List<string> items)
    {
        if (items == null ||
            items.Count == 0)
            return;

        for (int outerIndex = 0; outerIndex < items.Count; outerIndex++)
        {
            for (int innerIndex = 0; innerIndex < items.Count; innerIndex++)
            {
                if (innerIndex == outerIndex) continue;
                if (items[outerIndex].Equals(items[innerIndex]))
                {
                    // Duplicate Found
                }
            }
        }
    }

David J.의 답변은 좋은 방법이며, 추가 객체나 정렬 등이 필요하지 않습니다.그러나 다음과 같이 개선할 수 있습니다.

for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)

따라서 외부 루프는 전체 목록의 맨 아래가 되지만 내부 루프는 "외부 루프 위치에 도달할 때까지" 맨 아래가 됩니다.

외부 루프는 전체 목록이 처리되고 내부 루프가 실제 중복을 찾도록 합니다. 이러한 중복은 외부 루프가 아직 처리되지 않은 부분에서만 발생할 수 있습니다.

또는 내부 루프를 상향식으로 하고 싶지 않다면 내부 루프를 바깥쪽에서 시작할 수 있습니다.지수 + 1입니다.

간단한 직관적 구현입니다.

public static List<PointF> RemoveDuplicates(List<PointF> listPoints)
{
    List<PointF> result = new List<PointF>();

    for (int i = 0; i < listPoints.Count; i++)
    {
        if (!result.Contains(listPoints[i]))
            result.Add(listPoints[i]);
        }

        return result;
    }

모든 응답은 목록을 복사하거나 새 목록을 만들거나 느린 기능을 사용하거나 매우 느립니다.

제가 알기로는, 이 방법은 제가 아는 방법 중 가장 빠르고 저렴한 방법입니다(또한 실시간 물리학 최적화에 특화된 경험이 풍부한 프로그래머의 지원을 받기도 함).

// Duplicates will be noticed after a sort O(nLogn)
list.Sort();

// Store the current and last items. Current item declaration is not really needed, and probably optimized by the compiler, but in case it's not...
int lastItem = -1;
int currItem = -1;

int size = list.Count;

// Store the index pointing to the last item we want to keep in the list
int last = size - 1;

// Travel the items from last to first O(n)
for (int i = last; i >= 0; --i)
{
    currItem = list[i];

    // If this item was the same as the previous one, we don't want it
    if (currItem == lastItem)
    {
        // Overwrite last in current place. It is a swap but we don't need the last
       list[i] = list[last];

        // Reduce the last index, we don't want that one anymore
        last--;
    }

    // A new item, we store it and continue
    else
        lastItem = currItem;
}

// We now have an unsorted list with the duplicates at the end.

// Remove the last items just once
list.RemoveRange(last + 1, size - last - 1);

// Sort again O(n logn)
list.Sort();

최종 비용은 다음과 같습니다.

nlogn + n + nlogn = n + 2nlogn = O(nlogn)는 꽤 좋은 값입니다.

RemoveRange에 대한 참고: 목록 수를 설정할 수 없고 제거 기능을 사용하지 않기 때문에 이 작업의 속도를 정확히 알 수는 없지만 가장 빠른 방법인 것 같습니다.

HashSet을 사용하면 이 작업을 쉽게 수행할 수 있습니다.

List<int> listWithDuplicates = new List<int> { 1, 2, 1, 2, 3, 4, 5 };
HashSet<int> hashWithoutDuplicates = new HashSet<int> ( listWithDuplicates );
List<int> listWithoutDuplicates = hashWithoutDuplicates.ToList();

HashSet을 사용합니다.list = new HashSet<T>(list).ToList();

  public static void RemoveDuplicates<T>(IList<T> list )
  {
     if (list == null)
     {
        return;
     }
     int i = 1;
     while(i<list.Count)
     {
        int j = 0;
        bool remove = false;
        while (j < i && !remove)
        {
           if (list[i].Equals(list[j]))
           {
              remove = true;
           }
           j++;
        }
        if (remove)
        {
           list.RemoveAt(i);
        }
        else
        {
           i++;
        }
     }  
  }

복잡한 개체를 비교해야 하는 경우 Distinct() 메서드 내에 Comparer 개체를 전달해야 합니다.

private void GetDistinctItemList(List<MyListItem> _listWithDuplicates)
{
    //It might be a good idea to create MyListItemComparer 
    //elsewhere and cache it for performance.
    List<MyListItem> _listWithoutDuplicates = _listWithDuplicates.Distinct(new MyListItemComparer()).ToList();
        
    //Choose the line below instead, if you have a situation where there is a chance to change the list while Distinct() is running.
    //ToArray() is used to solve "Collection was modified; enumeration operation may not execute" error.
    //List<MyListItem> _listWithoutDuplicates = _listWithDuplicates.ToArray().Distinct(new MyListItemComparer()).ToList();

    return _listWithoutDuplicates;
}

다음과 같은 두 개의 다른 클래스가 있다고 가정합니다.

public class MyListItemComparer : IEqualityComparer<MyListItem>
{
    public bool Equals(MyListItem x, MyListItem y)
    {
        return x != null 
               && y != null 
               && x.A == y.A 
               && x.B.Equals(y.B); 
               && x.C.ToString().Equals(y.C.ToString());
    }

    public int GetHashCode(MyListItem codeh)
    {
        return codeh.GetHashCode();
    }
}

그리고요.

public class MyListItem
{
    public int A { get; }
    public string B { get; }
    public MyEnum C { get; }

    public MyListItem(int a, string b, MyEnum c)
    {
        A = a;
        B = b;
        C = c;
    }
}

가장 간단한 방법은 다음과 같습니다.

새 목록을 만들고 고유한 항목을 추가합니다.

예를 들어 다음과 같습니다.

        class MyList{
    int id;
    string date;
    string email;
    }
    
    List<MyList> ml = new Mylist();

ml.Add(new MyList(){
id = 1;
date = "2020/09/06";
email = "zarezadeh@gmailcom"
});

ml.Add(new MyList(){
id = 2;
date = "2020/09/01";
email = "zarezadeh@gmailcom"
});

 List<MyList> New_ml = new Mylist();

foreach (var item in ml)
                {
                    if (New_ml.Where(w => w.email == item.email).SingleOrDefault() == null)
                    {
                        New_ml.Add(new MyList()
                        {
                          id = item.id,
     date = item.date,
               email = item.email
                        });
                    }
                }

언급URL : https://stackoverflow.com/questions/47752/remove-duplicates-from-a-listt-in-c-sharp 입니다.