2009-12-16 2 views
4

개체 목록이 있습니다. 이러한 객체는 기본적으로 두 개의 문자열 필드 (String1 및 String2)를 포함하는 사용자 정의 클래스로 구성됩니다. 내가 알아야 할 것은 이러한 문자열 중 하나라도 해당 목록에 중복되어있는 경우입니다. 그래서 "objectA.String1 == objectB.String1"또는 "ObjectA.String2 == ObjectB.String2"또는 "ObjectA.String1 == ObjectB.String2"또는 "ObjectA.String2 == ObjectB"인지 알고 싶습니다. String1 ". 또한 중복 문자열을 포함하는 각 객체를 bool (HasDuplicate) 객체와 함께 표시하려고합니다.개체 목록에서 중복 속성을 검색하는 LINQ 쿼리

foreach(var item in duplicationList) 
{ 
    if(item.HasDuplicate == true) 
    { 
    Console.WriteLine("Duplicate detected!"); 
    } 
} 

이것은 LINQ와 함께 해결하는 좋은 문제처럼 seemd,하지만 난 내 인생은 알아낼 수 없습니다 위해 : 복제 탐지가 실행 된 경우

그래서 나는 단순히과 같이 목록을 통해 foreach는 할 좋은 쿼리. 그래서 '좋은 - 오래된'foreach를 사용하여 그것을 해결했지만 여전히 LINQ 버전에 관심이 있습니다.

답변

12

다음은 사례에 적합한 전체 코드 샘플입니다.

class A 
{ 
public string Foo { get; set; } 
public string Bar { get; set; } 
public bool HasDupe { get; set; } 
} 

var list = new List<A> { 
      new A{ Foo="abc", Bar="xyz"}, 
      new A{ Foo="def", Bar="ghi"}, 
      new A{ Foo="123", Bar="abc"} 
      }; 

var dupes = 
    list.Where(a => 
       list 
       .Except(new List<A>{a}) 
       .Any(x => x.Foo == a.Foo || x.Bar == a.Bar || x.Foo == a.Bar || x.Bar == a.Foo) 
    ).ToList(); 

dupes.ForEach(a => a.HasDupe = true); 
+0

LINQPad에서 테스트 할 때 작동합니다. 감사! –

+2

LINQPad는 이와 같은 문제를 파악하기위한 훌륭한 도구입니다. 모든 C# 개발자는 사본이 있어야합니다. –

+0

니스. 한 가지 요점 - Except에서 Any 메서드로 로직을 옮기는 것이 확인되는 모든 요소에 대한 List의 생성을 저장하므로 좀 더 효율적이라고 생각할 것입니다. var dupes = list.Where (a => 리스트 .Any (a! = x & Foo == a.Foo || x.Bar == a.Bar || x. Foo == a.Bar || x.Bar == a.Foo)) ) .ToList(); –

-1
var dups = duplicationList.GroupBy(x => x).Where(y => y.Count() > 1).Select(y => y.Key); 

foreach (var d in dups) 
    Console.WriteLine(d); 
+0

나는 다음과 같은 프로그램을 사용하여 LINQPad 당신에게 코드를 테스트 한 \t \t 새 TestObject ("3", "4"), \t \t 새 TestObject ("1", "6") \t}; \t \t VAR의 DUPS duplicationList.GroupBy = (X => X) 어디에요 (Y => y.Count()> 1) ALL 기타 사항 서보 -OFF (Y => y.Key); \t dups.Dump ("중복 덤프 :"+ dups.Count()); } Public 클래스 TestObject { \t 공개 TestObject (문자열 S1, S2 문자열) { \t \t \t 문자열 1 = S1; \t \t String2 = s2; \t \t IsDuplicate = false; \t} \t \t 공공 문자열 문자열 1; \t public string String2; \t 공개 bool IsDuplicate; 은} 이 작동하지 않습니다. dups에는 0 값이 들어 있습니다. –

0

개체가 아직 HasDuplicate 속성이없는 경우 첫째, HasDuplicateProperties 구현하는 확장 메서드 선언

public static bool HasDuplicateProperties<T>(this T instance) 
    where T : SomeClass 
    // where is optional, but might be useful when you want to enforce 
    // a base class/interface 
{ 
    // use reflection or something else to determine wether this instance 
    // has duplicate properties 
    return false; 
} 

당신은 쿼리에서 그 확장 방법을 사용할 수 있습니다 :

var itemsWithDuplicates = from item in duplicationList 
          where item.HasDuplicateProperties() 
          select item; 

동일한 속성 :

var itemsWithDuplicates = from item in duplicationList 
          where item.HasDuplicate 
          select item; 

또는

var itemsWithDuplicates = duplicationList.Where(x => x.HasDuplicateProperties()); 
+0

그건 내 질문이 아니야. 나는 내가 bool을 설정할 수 있도록 복제본을 결정하는 방법을 알고 싶었다. bool이 설정되면 목록에서 모든 객체를 가져 오는 방법을 알게됩니다. –

4

이 작동합니다 :

public class Foo 
{ 
    public string Bar; 
    public string Baz; 
    public bool HasDuplicates; 
} 

public static void SetHasDuplicate(IEnumerable<Foo> foos) 
{ 
    var dupes = foos 
     .SelectMany(f => new[] { new { Foo = f, Str = f.Bar }, new { Foo = f, Str = f.Baz } }) 
     .Distinct() // Eliminates double entries where Foo.Bar == Foo.Baz 
     .GroupBy(x => x.Str) 
     .Where(g => g.Count() > 1) 
     .SelectMany(g => g.Select(x => x.Foo)) 
     .Distinct() 
     .ToList(); 

    dupes.ForEach(d => d.HasDuplicates = true);  
} 

은 당신이 기본적으로하고있는 것은

  1. 가 SelectMany입니다 : 자신의 동반 푸와 함께, 모든 문자열의 목록을 만들
  2. Distinct : 동일한 Foo 인스턴스 (Foo.Bar == Foo.Baz)에 대한 중복 항목 제거
  3. GroupBy : 문자열로 그룹화
  4. 여기서 : 두 개 이상의 항목이있는 그룹을 필터링하십시오. 여기에는 중복 된 내용이 포함됩니다.
  5. SelectMany : 그룹에서 foos back을 가져옵니다.
  6. Distinct : foo가 목록에서 두 번 제거됩니다.
  7. ForEach : HasDuplicates 속성을 설정합니다. 윈스턴 스미스의 솔루션을 통해이 솔루션의

일부 장점은 다음과 같습니다 더 문자열 속성을 확장 할

  1. 쉽게. 5 개의 속성이 있다고 가정합니다. 그의 솔루션에서는 중복을 확인하기 위해 125 개의 비교를 작성해야합니다 (Any 절에서). 이 솔루션에서는 첫 번째 selectmany 호출에서 속성을 추가하는 것만 큼 문제가됩니다.
  2. 큰 목록의 경우 성능이 훨씬 향상되어야합니다. Winston의 솔루션은 목록의 각 항목에 대해 목록을 반복하며이 솔루션은 한 번만 반복합니다. (Winston의 솔루션은 O (n²) 인 반면 O (n²)입니다.
+0

그룹화가 그룹 구성원을 게으른 것으로 평가합니까? g.Skip (1) .ANY() g.Count에 비해 개선 될 수있다()> 1 – Jimmy

+0

@Jimmy 그룹이 게으르게 평가되지 않기 때문에 정말,이 경우에는 문제가되지 않습니다. 나는 Skip (1)을 좋아한다. 아무거나() 트릭. 내 자신의 프로젝트의 , 나는 항상 확장 방법을 (예상 int)를 CountIs, CountIsGreaterThan을 (INT 예상)이 ...응답을 알 자마자 평가를 중단합니다. –

0

에서 모자 팁

var duplicates = duplicationList 
       .GroupBy(l => l) 
       .Where(g => g.Count() > 1) 
       .Select(g => {foreach (var x in g) 
           {x.HasDuplicate = true;} 
          return g; 
       }); 

duplicateshttps://stackoverflow.com/a/807816/492에 일회용이지만 적은 열거 거기에 당신을 가져옵니다. 무효 메인() { \t var에 duplicationList = 새로운 목록 \t { \t \t 새 TestObject ("1", "2"), :