2017-09-23 3 views
1

저는 Linq를 처음 사용하면서도 익숙해 지려고합니다. 나는 중복을 찾기 위해 linq 쿼리를 가지고 있으며 아래처럼 완벽하게 작동합니다.포함 된 linq 사용 - 오류 가져 오기

// "MergedName"은 쿼리하고 중복 이름을 찾는 데이터 열입니다.

var duplicates = result.AsEnumerable() 
      `.Select(dr => dr.Field<string("MergedName").Replace("'", "''")) 
      .GroupBy(x => x) 
      .Where(g => g.Count() > 1) 
      .Select(g => g.Key) 
      .ToList(); 

    foreach (string duplicate in duplicates.ToArray()) 
      { 
       // Logic to keep one and delete another duplicate. 
      } 

이제 동일한 이름 "MergedName"에서 유사 이름을 찾고 싶습니다. 예를 들어 : 존 스미스와 존 스미스 주니어 나는 어디에요 절 뭔가를 쓴하지만 뭔가 내 구문

var duplicates = result.AsEnumerable() 
       .Select(dr => dr.Field<string>("MergedName").Replace("'", "''")) 
       .Where(C => C.Field<string>("MergedName").ToLower().IndexOf(C.Field<string>("MergedName").ToLower().Trim()) != 1) 
       .Select(g => g.Key) 
       .ToList(); 

foreach (string duplicate in duplicates.ToArray()) 
      { 
       // Logic to keep one and delete another duplicate. 
      } 

오류에 문제가 있습니다 : 문이 어디에 - "문자열이 '필드'에 대한 정의가 포함되어 있지 않습니다 최고의 확장 메서드 오버로드 'System.Data.DatarowExtensions.Field는 일부 잘못된 인수가'.

는이 코드? 아니면 내가 포함하여 비슷한 이름을 찾을 수있는 다른 방법을 좀 도와 주시겠습니까.

답변

0

원래 수집 유형을 게시하지 않고도 말하기가 어렵습니다. ,하지만 문제는 정확하게 오류 메시지에 명시된 것 같습니다.

LINQ는 반복 단계에서 작동하며 Select(dr => dr.Field<string>("MergedName").Replace("'", "''"))을 호출하면 다음 expereion이 문자열 모음으로 작동합니다. 어떤이이기 때문에 문자열 유형에 아무런 방법 .Field

나는, 당신은 또한 그래서 당신의 두 번째 문에 Select(g => g.Key) 못해 일을 어떤 그룹화를 수행하지 않는 C.ToLower()

에 where 절에 C.Field<string>("MergedName").ToLower()을 단순화 시도 할 수있다 생각하지 String 형의 Key 프로퍼티.

이 구문 만 처리하면 Where 절이 여전히 이상하게 보입니다. 각 문자열을 자체와 비교하고 있습니다.

당신은 그냥 마지막 문장의 어떤 부분에 자신의 상태를 넣어

var names = result.AsEnumerable() 
      .Select(dr => dr.Field<string("MergedName").Replace("'", "''").ToLower().Trim()) 
      .ToList(); //ToList not necessary here, but could prevent multiple executions of the expresion 


var duplicates = names.Where(n => names.Any(m => n.IndexOf(m) != -1)) //quadratic complexity 
      .ToList(); 

같은 것을 시도해 볼 수도, 당신이 두 개의 문자열 mn이 있고 그러나 당신이 원하는을 비교할 수 있습니다.
이것은 확실히 문제를 해결하는 최상의 솔루션은 아니지만 questinon에서와 같이 LINQ를 사용하므로 작성하고 이해하기 쉽습니다. 설명 후

:

dr.Field<string>("MergedName").Replace("'", "''").Trim().ToLower().IndexOf(dr2.Field<string>("MergedName").Replace("'", "''").Trim().ToLower()) != -1 

이 조건은 귀하의 질문에 하나,하지 귀하의 코멘트에있는 한을 기반으로합니다

var enumerableResult = result.AsEnumerable(); 
var duplicates = enumerableResult. 
       .Where(dr => enumerableResult.Any(dr2 => /*your comparison*/) 
       .ToList(); 

비교 식으로 뭔가를 할 수 있습니다. 하지만이 인라인 sytax를 사용할 필요가 없으므로 사용자 정의 메서드를 호출 할 수 있습니다. .Any(dr2 => AreSamePerson(dr, dr2))

이것은 다시 2 차적인 복잡성이 있습니다. 비교할 레코드가 많은 경우에만 문제가됩니다.

이제 문자열 대신에 사람 객체의 컬렉션을 가져옵니다.원래 복제 컬렉션의 구성원을 삭제할 수는 없지만 다소 복잡한 논리가 필요하다는 것을 명심하십시오.

그래서 최선의 해결책이 될 것으로 보인다 : 이것은 당신의 동일한 기능을 symetric 것을 문제로 이어질, 그렇게 확신 할 수

var duplicates = result.AsEnumerable() 
      .GroupBy(x => x, new PersonyComparer()) 
      .Where(g => g.Count() > 1) 

class PersonyComparer : IEqualityComparer<Person>//person is the type of objects that are in starting collection 
    { 
     public bool Equals(Person b1, Person b2) 
     { 
      if (b2 == null && b1 == null) 
       return true; 
      else if (b1 == null | b2 == null) 
       return false; 


      if(/*your condition*/) 
       return true; 
      else 
       return false; 
     } 

     public int GetHashCode(Person bx) 
     { 
      return 0; //you must make sure that objects that are equal have same hashcode 
     } 
    } 

(경우 다음 B A == == B A)과 전이 (있는 경우 a == b이고 b == c이면 a == c). 그렇지 않으면 당신의 groupping 조금 망쳐 놓을 수 있습니다.

그럼 당신은

foreach(var pgroup in duplicates) 
{ 
    foreach(var person in pgroup .Skip(1)) 
    { 
     //remove from original collection 
    } 
} 
+0

Noxor - 위의 솔루션에 감사드립니다. 올바른 방향으로 나아갑니다. 나는 "John Matt Smith"이고 다른 기록은 "John Matthew Smith"인 같은 이름의 두 사람의 이름을 가지고 있는데, 나는 그들이 같은 사람이라는 것을 알고 있으므로 그들 중 하나를 지우고 싶다. 이것이 이유입니다. Any 대신 Contains를 사용하려고했습니다.이 링크에 대한 링크 : "https://stackoverflow.com/questions/23526773/what-is-the-difference-between-contains-and-any-in- linq ". 비슷한 이름을 먼저 쿼리하여 목록에 넣을 수있는 방법이 있습니까? 이것을 더 일찍 명확히하지 않는 것에 대한 사과. –

+0

나는 객체의 equal 메소드를 사용하고 있다고 생각한다. 그래서 당신은 그것을 대체 할 수있다. 그래서 비슷한 이름을 가진 객체는 true를 반환 할 것이다. 그러나 이것은 정말 나쁜 생각처럼 보입니다. 원래 컬렉션에서 "복제"레코드를 삭제하려면 문자열뿐만 아니라 복제 된 객체의 컬렉션을 유지하십시오. 내 대답을 편집하십시오. – Noxor

+0

당신은 그들이 같은 사람임을 어떻게 알 수 있습니까? 사회 보장 번호와 같은 다른 재산이 있다면 이름 대신 그루핑하는 것이 더 좋을 것입니다. – Noxor

0

당신이 원하지해야하는 이유를 나에게 모범을 보여주지 중복 수집의 목적을 반복 할 수 있습니다. Noxor가 올바르게 말한대로 실행 가능한 접근법은 IEqualityComparer을 사용하는 것입니다. 그러나 이제 질문은 : 평등이란 무엇입니까? 당신의 "평등을 담아"는 당신이 해결할 수없는 어렴풋을 소개합니다.

가장 기본적인 방법으로 사례와 문자열 교체를 잊어 버리겠습니다. 이 작은 Linqpad 프로그램을보십시오 :

void Main() 
{ 
    var dt = new DataTable(); 
    dt.Columns.Add("MergedName", typeof(string)); 

    dt.Rows.Add("Abby Kelley Foster"); 
    dt.Rows.Add("Kelley Foster"); 
    dt.Rows.Add("Abby Kelley"); 

    dt.AsEnumerable() 
     .Select(r => r.Field<string>("MergedName")) 
     .GroupBy(s => s, new SubstringComparer()) 
     .Select(g => new { g.Key, Count = g.Count() }) 
     .Dump(); 

} 

public class SubstringComparer : IEqualityComparer<string> 
{ 
    public bool Equals(string left, string right) 
    { 
     return left.Contains(right) || right.Contains(left); 
    } 

    public int GetHashCode(string value) 
    { 
     return 0; // Just return 0; There is no hashing mechanism implemented that gives "Abby Kelley Foster" and "Abby Kelley" the same hashcode. 
    } 
} 

출력은 무엇입니까? 오른쪽 :

Abby Kelley Foster 3 

그러나의 현재 데이터 행의 순서를 변경할 수 있습니다 :

dt.Rows.Add("Abby Kelley"); 
    dt.Rows.Add("Kelley Foster"); 
    dt.Rows.Add("Abby Kelley Foster"); 

당신이 출력을 공제 할 수 있나요? 여기에 있습니다 :

Abby Kelley 1 
Kelley Foster 2 

Abby Kelley Foster은 어떻게 되었습니까?

비교자가 첫 번째로 불평등 행 두 개를 만났습니다. Abby Kelley의 경우 1로 간주되고 Kelley Foster와 Abby Kelley Foster : Bingo를 비교했습니다. "같은". 그러나이 시점에서 첫 번째 행으로 돌아와서 세 번째 행과 비교하지 않습니다.

당신은 모든 행을 비교하는보다 정교한 (하지만 여전히 간단한) 알고리즘을 시도해 볼 수도 있습니다,하지만 당신은 여전히 ​​잘못된

Abby Kelley Foster 3 

를 얻을 수 있습니다. Abby Kelley와 Abby Kelley Foster만이 같은 사람입니다. 켈리 포스터는 완전히 다른 사람입니다. 즉, 자동화 된 알고리즘으로는이를 해결할 수 없습니다. 정확한 동등성 만 간단한 알고리즘으로 결정할 수 있습니다.

인위적인 예제로이 집을 치기 위해서는 : "Jr."한 항목 만 있다고 가정 해보십시오. 이제 모든 이름이 "Jr." 중복으로 볼 수 있습니다!

+0

Gert Arnold - 동의합니다. 당신이 제공 한 예제는 내가 만났을 때 내 데이터에서 생각한 것입니다. –

+0

좋아, 너는 그걸 생각했을지 모르지만, 나에게 유일한 결론은 너는 이렇게 갈 수 없다는 것이다. 아직 세 번째 출력없이 그룹화되지 않은 두 개의 이름이 포함되어 있기 때문에 첫 번째 출력 (3)이 논리적으로 올바르지 않다는 사실을 언급하지 않았습니다. –

관련 문제