2017-09-23 98 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)在逻辑上不正确的事实,因为它包含两个名字,如果没有第三个名字,那么它们就不会被分组。 –