Linq:按 OR 条件分组

Posted

技术标签:

【中文标题】Linq:按 OR 条件分组【英文标题】:Linq: Group by with OR condition 【发布时间】:2019-08-02 12:49:15 【问题描述】:

以下是我们尝试按以下OR 条件对记录进行分组的记录:

    名字相同 邮箱是一样的 手机是一样的

在 LINQ 中有没有办法在 Group By 条件下使用 Or 条件?

Name           Email            Phone             Id
---            ---------        ------------      ----------
Rohan          rohan@s.com      NULL              1  
R. Mehta       rohan@s.com      9999999999        2
Alex           alex@j.com       7777777777        3  
Lisa John      john@j.com       6666666666        4
Lisa           lisa@j.com       6666666666        5
Siri           siri@s.com       NULL              6
RM             info@s.com       9999999999        7
Lisa           NULL             NULL              8
Lisa John      m@s.com          7777777757        9

预期输出

Group 1:
Key: Rohan
RecordIds: 1,2,7  (As `Id:1` has same email as `Id:2`, `Id:2` has same 
                    phone number as `Id:7`.)

Group 2:
Key: Lisa John
RecordIds: 4,5,8,9  (As `Id:4` has same phone number as `Id:5`. While `Id:5` 
                    has the same name as `Id:8`. As `Id:9` has the same name 
                    as `Id: 4`, include that)
    3 和 6 不是输出的一部分,因为输出只是包含 1 条以上记录的组 密钥可以是我刚刚放入随机密钥的任何内容。

如果记录 9 具有电子邮件 ID:rohan@s.com,则:

输出

Group 1:
Key: Rohan
RecordIds: 1,2,7,4,5,8,9

注意: 输入是SQL table,可以通过LINQ to SQL 读取。所以查询性能也必须考虑在内。

粗略的解决方案

一个肮脏的解决方案如下:

    按名称对记录进行分组 -> 将结果存储在var gl-1 通过电子邮件对记录进行分组 -> 将结果存储在var gl-2 通过电话对记录进行分组 -> 将结果存储在var gl-3gl-1 中获取每个结果,检查gl-2,gl-3 中是否存在对应的id。如果是这样,将那些ids 包括在gl-1 中 获取gl-2 中的每个结果,检查gl-1 中的任何结果中是否存在对应的id,包括唯一的idsgl-1 记录。如果循环遇到gl-1 中不存在的结果,请将其作为结果包含在gl-1 中。 为gl-3 执行第 5 步。

【问题讨论】:

Group By Multiple Columns的可能重复 同意@MattRowland。这更像是这个 SQL 问题的 LINQ 版本:***.com/questions/10763043/… 查看***.com/questions/19703034/… OK - 根据您的示例数据 - 您将无法使用 Entity Framework 或 LINQ to SQL 解决该问题。您将不得不将其拉入 RAM 并使用 C# 解决它。 当您在图表中处理连接时,this 可以为您提供答案 【参考方案1】:

GroupBy 需要一些“平等”的定义。你可以用你想要的逻辑定义一个EqualityComparer,但是你会得到不一致的结果。您的分组打破了分组所需的等式传递属性。换句话说,如果A=BB=CA=C 必须为真。

例如,以下成对的项目将在同一组中(“相等”):

A, B, C  and  A, D, E
A, D, E  and  F, G, E

但是

A, B, C  and  F, G, E

不会在同一组中。

要获得您想要的输出(例如,多个组中的第 9 项),您需要使用标准循环来递归查找与第一个“相等”的所有项,然后找到与该组“相等”的所有项,然后是与第三组“相等”的所有项目,等等。Linq 在这里不会很有帮助(除了可能在每个递归调用中进行搜索)。

【讨论】:

【参考方案2】:

Linq 查询是线性运行的,这意味着一旦它通过了一个新的可能组,它就无法返回并使用它。

假设

 public class aperson

    public string Name;
    public string Email;
    public string Phone;
    public int ID;

    public aperson(string name,string email,string phone,int id)
    
        Name = name;
        Email = email;
        Phone = phone;
        ID = id;
    

例子

 new aperson("a","a@","1",1),
 new aperson("b","b@","2",2),
 new aperson("a","c@","2",3)

迭代 1:使用 ("a","a@","1") 值创建组 1 迭代 2:使用 ("b","b@","2") 值创建组 2 迭代 3:这里系统必须将其与组 1 或组 2 进行分组,但不能同时将其与组 1 或组 2 组合。

要解决此问题,您的迭代器必须返回组 2 和组 1 并加入它们。

要解决此问题,您必须将其分解为多个步骤。

步骤 1。创建组

步骤 2。按创建的组分组。

我认为有更好的方法来做到这一点。我只是在说明需要如何解决这个问题以及为什么要解决这个问题的流程。

解决方案代码

    public static Dictionary<string, int> group = new Dictionary<string, int>();

    public static void adduniquevalue(aperson person,int id)
    

        if (person.Email != null && !group.Keys.Contains(person.Email))
        
            group.Add(person.Email, id);
        
        if (person.Phone != null && !group.Keys.Contains(person.Phone))
        
            group.Add(person.Phone, id);
        
        if (person.Name != null && !group.Keys.Contains(person.Name))
        
            group.Add(person.Name, id);
        
    

    public static void CreateGroupKeys(aperson person)
    
        int id = group.Count;
        List<int> groupmatches = new List<int>();
        if (person.Email != null && group.Keys.Contains(person.Email)) 
            groupmatches.Add(group[person.Email]);  
        if (person.Phone != null && group.Keys.Contains(person.Phone)) 
            groupmatches.Add(group[person.Phone]); 
        if (person.Name != null && group.Keys.Contains(person.Name)) 
            groupmatches.Add(group[person.Name]); 
        if (groupmatches.GroupBy(x=>x).Count() > 1)
        
            int newid = groupmatches[0];
            group.Keys.Where(key => groupmatches.Contains(group[key]))
                      .ToList()
                      .ForEach(key =>  group[key] = newid; ); 
        
        if (groupmatches.Count == 0)
          adduniquevalue(person, id);
        else adduniquevalue(person, groupmatches[0]);
    

    public static int GetGroupKey(aperson person)
    
        if (person.Email != null && group.Keys.Contains(person.Email))
            return group[person.Email]; 
        if (person.Phone != null && group.Keys.Contains(person.Phone))
            return group[person.Phone]; 
        if (person.Name != null && group.Keys.Contains(person.Name))
            return group[person.Name];
        else return 0;
    

这将在字典中创建您的组,稍后您可以通过方法在普通组中使用该组。

像这样:

 people.ForEach(x => CreateGroupKeys(x));
 var groups = people.GroupBy(x => GetGroupKey(x)).ToList();

【讨论】:

以上是关于Linq:按 OR 条件分组的主要内容,如果未能解决你的问题,请参考以下文章

LINQ - 按多个键分组未给出预期结果

LINQ 按许多值分组

linq.js 按javascript中的对象数组分组

LINQ 按多个字段分组 - 语法帮助

如何使用linq按多列分组[重复]

C# LINQ - 按属性分组列表,然后按不同组选择