2017-09-23 97 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. 
      } 

錯誤:WHERE語句 - 「字符串不包含定義‘現場’和最佳推廣方法重載「System.Data.DatarowExtensions.Field有一些無效的參數」。

能否請你幫我這個代碼嗎?或任何其他方式在那裏我可以使用包含找到相似的名稱。

回答

0

沒有你張貼原始集合的類型很難說,但問題似乎正是你的錯誤信息所述。

LINQ工作在迭代步驟,並呼籲Select(dr => dr.Field<string>("MergedName").Replace("'", "''"))後,你接下來expresion適用於字符串的集合。還有在字符串類型,沒有方法.Field

我想你可以試試你的where子句中簡化C.Field<string>("MergedName").ToLower()C.ToLower()

您還沒有做任何分組在你的第二個語句,以便Select(g => g.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那裏,但是你希望可以對它們進行比較。
這絕對不是您問題的最佳解決方案,但它使用LINQ,因爲它在您的questinon中很容易編寫和理解。

澄清後:

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

比較可能是這樣的:

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

這個條件是根據一個在你的問題,而不是一個在您的評論。但是,你不需要使用內聯和語法時纔可以調用一些自定義的方法,所以它看起來像.Any(dr2 => AreSamePerson(dr, dr2))

這有再次二次複雜性,問題只有當你有很多的記錄進行比較。

現在,您可以獲取人物對象的集合,而不僅僅是字符串。請記住,你不能從原始的集合中刪除重複集合的成員,但需要一些相當複雜的邏輯。

所以最好的解決辦法似乎是:

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則b == a)和傳遞(如果a == b和b == c然後a == c)。否則你的團隊可能會被搞砸。

然後你就可以在重複徵收的對象

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

Noxor - 感謝您對以上解決方案,它可以幫助在正確的方向。我有兩個名字,這兩個名字在集合中是同一個人:「John Mat Smith」,另一個記錄是「John Matthew Smith」,我知道他們是同一個人,所以想刪除其中的一個。這就是原因,我正在尋找使用Contains而不是Any.Referred to this link:「https://stackoverflow.com/questions/23526773/what-is-the-difference-between-contains-and-any-in- LINQ」。有沒有辦法我可以先查詢相似的名字並將它們放入列表中?對於之前沒有澄清這一點抱有歉意。 –

+0

我猜包含使用相同方法的對象,所以你可以重寫,所以具有類似名稱的對象將返回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怎麼了?

的比較器第一次遇到這兩個第一個不平等行,計1阿比·凱利和繼續比較凱利福斯特和阿比·凱利福斯特:賓果! 「等於」。但是,在這一點上,它永遠不會返回到第一行來比較它與第三行。

你可以嘗試更復雜(但依然簡單)的算法,比較所有的行,但你會得到

Abby Kelley Foster 3 

仍然是錯誤的。只有艾比凱利和艾比凱利福斯特是同一個人。凱利福斯特完全是別人。換句話說:您無法通過任何自動算法解決此問題。只有精確的相等才能通過簡單的算法來確定。

爲了打這個家有一個人爲的例子:假設一個項目是什麼,但「小」。現在所有名稱都帶有「Jr.」將被視爲重複!

+0

格特阿諾德 - 同意。您提供的示例是我在數據中遇到和想到的示例。 –

+0

好的,你可能已經想到了,但對我來說唯一的結論就是你不能這樣下去。我還沒有提到第一個輸出(3)在邏輯上不正確的事實,因爲它包含兩個名字,如果沒有第三個名字,那麼它們就不會被分組。 –