Linq:按或条件分组

时间:2019-08-02 12:49:15

标签: c# linq

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

  1. 名称相同
  2. 电子邮件是相同的
  3. 电话是相同的

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)
  1. 3和6不是输出的一部分,因为输出是只有多于1条记录的组
  2. 密钥可以是我刚刚放入随机密钥中的任何内容。

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

输出

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

注意::输入为SQL table,将通过LINQ to SQL进行读取。因此,也必须考虑查询性能。

原始解决方案

以下是一种肮脏的解决方案:

  1. 按名称对记录分组->将结果存储在var gl-1
  2. 通过电子邮件将记录分组->将结果存储在var gl-2
  3. 通过电话将记录分组->将结果存储在var gl-3
  4. gl-1中获取每个结果,以检查idgl-2中是否存在相应的gl-3。如果是这样,请在ids
  5. 中加入gl-1
  6. gl-2中获取每个结果,以检查id中的任何结果中是否存在对应的gl-1,因此,将独占的ids包含到gl-1记录中。如果循环遇到gl-1中不存在的结果,请将其作为结果包含在gl-1中。
  7. gl-3执行步骤5。

2 个答案:

答案 0 :(得分:2)

GroupBy需要对“平等”进行一些定义。您可以使用所需的逻辑 定义一个EqualityComparer,但结果会不一致。您的分组破坏了分组所需的相等性的传递属性。换句话说,如果A=BB=C,则A=C 必须为真。

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

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

但是

A, B, C  and  F, G, E

不会在同一组中。

要获得所需的输出(例如,多个组中的第9项),您需要使用标准循环来递归地找到与第一个“相等”的所有项,然后再找到与该组“相等”的所有项,则所有与第三组“相等”的项目都以此类推,以此类推。Linq在这里并不是很有用(除非可能是在每个递归调用中进行搜索)。

答案 1 :(得分:0)

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分组,但不能同时分组。

要解决此问题,您的迭代器必须回到第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();