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


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.