使用LINQ / C#查询检测“接近重复”

时间:2012-11-05 12:14:57

标签: c# linq duplicates

我正在使用以下查询来检测数据库中的重复项。

使用LINQ连接不能很好地工作,因为公司X也可能被列为CompanyX,因此我想修改它以检测“接近重复”。

var results = result
                .GroupBy(c => new {c.CompanyName})
                .Select(g => new CompanyGridViewModel
                    {
                        LeadId = g.First().LeadId,
                        Qty = g.Count(),
                        CompanyName = g.Key.CompanyName,
                    }).ToList();

有人能建议我更好地控制比较吗?也许是通过IEqualityComparer(虽然我不确定在这种情况下它是如何工作的)

我的主要目标是:

  1. 列出包含所有重复项子集的第一条记录(或“近似重复”)
  2. 对我用于复制的字段和文本比较有一定的灵活性。

3 个答案:

答案 0 :(得分:1)

好的,所以既然你正在寻找不同的排列,你可以这样做:

  

请记住这是在答案中写的,所以它可能无法完全编译,但你明白了。

var results = result
    .Where(g => CompanyNamePermutations(g.Key.CompanyName).Contains(g.Key.CompanyName))
    .GroupBy(c => new {c.CompanyName})
    .Select(g => new CompanyGridViewModel
        {
            LeadId = g.First().LeadId,
            Qty = g.Count(),
            CompanyName = g.Key.CompanyName,
        }).ToList();

private static List<string> CompanyNamePermutations(string companyName)
{
    // build your permutations here
    // so to build the one in your example
    return new List<string>
    {
        companyName,
        string.Join("", companyName.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
    };
}

答案 1 :(得分:1)

对于明确的“忽略空格”案例,您只需调用

即可
var results = result.GroupBy(c => c.Name.Replace(" ", ""))...

但是,在您需要灵活性的一般情况下,我会建立一个IEqualityComparer<Company>类库,以便在您的分组中使用。例如,这应该在“忽略空间”的情况下执行相同的操作:

public class CompanyNameIgnoringSpaces : IEqualityComparer<Company>
{
    public bool Equals(Company x, Company y)
    {
        return x.Name.Replace(" ", "") == y.Name.Replace(" ", "");
    }

    public int GetHashCode(Company obj)
    {
        return obj.Name.Replace(" ", "").GetHashCode();
    }
}

你可以用作

var results = result.GroupBy(c => c, new CompanyNameIgnoringSpaces())...

执行包含多个字段或其他相似性定义的类似内容非常简单。

请注意,您对“类似”的定义必须是可传递的,例如如果你正在查看整数,你就不能将“相似”定义为“在5以内”,因为那时你会得到“0类似于5”而“5类似于10”而不是“0类似于10” ”。 (它也必须是反身和对称的,但这更直接。)

答案 2 :(得分:1)

在这种情况下,您需要定义工作的位置,即完全在服务器上,本地内存中或两者的混合中。

  1. 在本地记忆中: 在这种情况下,我们有两条路径,用于拉回所有数据,只是在本地存储器中执行逻辑,或者流式传输数据并分段应用逻辑。仅提取基表的所有数据ToList()ToArray()。要传输数据,建议将ToLookup()与自定义IEqualityComparer一起使用,例如

    public class CustomEqualityComparer: IEqualityComparer<String>
    {
        public bool Equals(String str1, String str2)
        {
            //custom logic
        }
    
        public int GetHashCode(String str)
        {
            // custom logic
        }
    }
    
    //result
    var results = result.ToLookup(r => r.Name, 
                        new CustomEqualityComparer())
                        .Select(r => ....)
    
  2. 完全在服务器上: 取决于您的提供商以及它可以成功映射的内容。例如。如果我们将近似副本定义为具有替代定界符的副本,则可以执行以下操作:

    private char[] delimiters = new char[]{' ','-','*'}
    
    var results = result.GroupBy(r => delimiters.Aggregate( d => r.Replace(d,'')...
    
  3. 混合物: 在这种情况下,我们正在分裂两者之间的工作。除非你想出一个好的方案,否则这条路线很可能效率低下。例如。如果我们将逻辑保留在本地,将分组构建为从名称到密钥的映射,并只查询结果分组,我们可以这样做:

    var groupings = result.Select(r => r.Name)
                          //pull into local memory
                          .ToArray()
                          //do local grouping logic...
    
                          //Query results
    var results = result.GroupBy(r => groupings[r]).....
    
  4. 就我个人而言,我通常选择第一个选项,为小数据集和流式大数据集提取所有数据(根据经验,我发现每次拉动之间的逻辑流比拉动所有数据然后执行所有逻辑要花费更长的时间)

    注意:依赖于提供者ToLookup()通常是立即执行,并且在构造中分段应用其逻辑。