목록에서 빈 문자열을 제거한 다음 목록에서 중복 값을 제거하는 방법


82

테이블에서 가져온 일부 열 값 목록이 있다고 가정 해 보겠습니다. 빈 문자열과 중복 값을 제거하는 방법은 무엇입니까? 다음 코드를 참조하십시오.

List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();

이것이 내가 방금 코딩 한 것이지만 Amiram의 코드가 훨씬 더 우아하므로 여기에서 내가 어떻게했는지 답을 선택할 것입니다.

DataTable dtReportsList = someclass.GetReportsList();

        if (dtReportsList.Rows.Count > 0)
       { 
           List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();
           dtList.RemoveAll(x=>x == "");
           dtList = dtList.Distinct().ToList();         

           rcboModule.DataSource = dtList;
           rcboModule.DataBind();               
           rcboModule.Items.Insert(0, new RadComboBoxItem("All", "All"));
       }

RemoveAll ()이 dtList를 변경한다는 것을 이해하십시오. 제거되는 각 요소는 List가 사용하는 기본 배열에서 상위 인덱스의 요소를 재정렬하도록합니다. Amiram이 Where 방법으로 수행하는 것처럼 단순히 건너 뛰는 것이 더 빠를 것입니다.
— KeithS

답변:


201
dtList  = dtList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList()

빈 문자열과 공백은 null과 같다고 가정했습니다. 그렇지 않은 경우 사용할 수 있습니다 IsNullOrEmpty(공백 허용).s != null


단 하나; Distinct ()를 사용한 중복 제거는 메서드가 최악의 경우를 가정해야하기 때문에 상대적으로 비효율적입니다.
— KeithS

@KeithS Distinct최적화 할 수없는 이 데이터에 대해 어떤 주장을 알고 있습니까?
— Servy

목록을 정렬 한 다음 정렬되었다고 주장하여 중복 제거 알고리즘을 선형으로 만들 수 있습니다. 내 대답을 참조하십시오.
— KeithS

9

Amiram의 대답은 정확하지만 구현 된 Distinct ()는 N 2 연산입니다. 목록의 각 항목에 대해 알고리즘은 이미 처리 된 모든 요소와 비교하여 고유 한 경우 반환하거나 그렇지 않은 경우 무시합니다. 우리는 더 잘할 수 있습니다.

분류 목록은 선형 시간에 deduped 할 수있다; 현재 요소가 이전 요소와 같으면 무시하고 그렇지 않으면 반환합니다. 정렬은 NlogN이므로 컬렉션을 정렬해야하는 경우에도 다음과 같은 이점이 있습니다.

public static IEnumerable<T> SortAndDedupe<T>(this IEnumerable<T> input)
{
   var toDedupe = input.OrderBy(x=>x);

   T prev;
   foreach(var element in toDedupe)
   {
      if(element == prev) continue;

      yield return element;
      prev = element;      
   }
}

//Usage
dtList  = dtList.Where(s => !string.IsNullOrWhitespace(s)).SortAndDedupe().ToList();

이것은 동일한 요소를 리턴합니다. 그들은 단지 정렬되어 있습니다.


큰. 내가 틀린 것이 아니라면 실제로 주문을 수행하는 요소를 반복함으로써. 방법을 "게으른"방법을 생각할 수 있습니까?
— Amiram

불행히도 대부분의 분류는 분류 할 전체 컬렉션에 대한 지식이 필요합니다. 가장 마지막 요소는 반환되어야하는 첫 번째 요소가 될 수 있습니다. 따라서 입력의 모든 요소를 ​​평가하여 출력의 첫 번째 요소를 생성해야합니다. 출력의 다음 요소를 찾은 후에 중단 될 수 있다고 생각할 수있는 유일한 정렬은 SelectionSort 변형이며,이 경우 시작점으로 돌아 왔습니다.
— KeithS

게다가, 우리의 경우 전체 작업의 결과가 목록이므로 시작하려면 "열심히"실행해야합니다. IEnumerable로 작업하고 실행을 연기하려면 함수의 핵심을 가져와 IEnumerable을 구현하는 숨겨진 Iterator 클래스에 넣을 수 있습니다.
— KeithS

Distinct해싱을 사용하며 O (N ^ 2)보다 O (N)에 더 가까워 야합니다. 소스
— 위험한 마틴

... 글쎄, 난 감히, 정말 그렇습니다. System.Linq.Set은 Distinct에서 사용하는 내부 해시 테이블 구현으로, 항목의 GetHashCode () 구현이 효율적이고 균등하게 분산 된 해시를 생성 한다고 가정 할 때 O (1) 액세스 시간에 가깝습니다 (기본 구현에서 그렇게 함). . 그러나 해시 테이블에는 메모리 문제가 있습니다. .NET의 기본 구현은 int 중 하나와 연결된 항목 중 하나 인 두 개의 배열을 사용합니다. 각각은 세트의 항목 수와 같고 최악의 경우 두 배입니다.
— KeithS

1

Amiram Korach 솔루션은 실제로 깔끔합니다. 다재다능 함을위한 대안이 있습니다.

var count = dtList.Count;
// Perform a reverse tracking.
for (var i = count - 1; i > -1; i--)
{
    if (dtList[i]==string.Empty) dtList.RemoveAt(i);
}
// Keep only the unique list items.
dtList = dtList.Distinct().ToList();

4
이것이 작동하는 동안 Where 절은 입력 컬렉션을 변경할 필요가 없기 때문에 더 빠릅니다. 목록에서 요소를 제거 할 때 수행해야하는 "이동"횟수를 최소화하고 있지만 Where는 입력에서 아무것도 제거하지 않습니다. 일치하지 않는 요소를 건너 뜁니다.
— KeithS

0

Amiram Korach의 솔루션 을 단순화하려면 :

dtList.RemoveAll(s => string.IsNullOrWhiteSpace(s))

Distinct () 또는 ToList ()를 사용할 필요가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.