使用逻辑或对多个属性进行分组

Group Multiple Properties with Logical OR

我有一个 class 具有多个属性,我对其中两个感兴趣。例如,在下面的示例中 PropAPropB

public class GroupByOR
{
    public string PropA { get; set; }
    public string PropB { get; set; }
    public int SomeNumber { get; set; }
    public GroupByOR(string a, string b, int num) { PropA = a; PropB = b; SomeNumber = num; }
}

我想对这个 class' 对象的列表进行分组,如果 either PropA 或者PropB 匹配。

例如,假设我的列表如下所示:

List<GroupByOR> list = new List<GroupByOR>
{
    new GroupByOR("CA", "NY", 1), // Item 1
    new GroupByOR("CA", "OR", 2), // Item 2
    new GroupByOR("NY", "OR", 5)  // Item 3
};

那么我想要的结果是这样的:

Group 1: Items 1 and 2 (based on CA)

Group 2: Items 1 and 3 (based on NY)

Group 3: Items 2 and 3 (based on OR)

环顾四周,我发现了 this 和许多其他示例,但它们似乎都专注于按多个属性进行分组,但使用 AND 操作。

我想要实现的目标是否可行?

否则 join 是去这里的路吗?如果是这样,你能给我指明正确的方向吗?

您最终会得到比开始时更多的项目,因此 GroupBy 并不是全部答案。你需要这样的东西:

List<GroupByOR> list = new List<GroupByOR>
{
    new GroupByOR("CA", "NY", 1), // Item 1
    new GroupByOR("CA", "OR", 2), // Item 2
    new GroupByOR("NY", "OR", 5)  // Item 3
};

var lookupA = list.ToLookup(e => e.PropA);
var lookupB = list.ToLookup(e => e.PropB);
var keys = lookupA.Select(e => e.Key)
    .Concat(lookupB.Select(e => e.Key)).Distinct();

var result = keys.Select(e => 
    new 
    { 
        Key = e, 
        Values = lookupA[e].Concat(lookupB[e]).Distinct()
    });

这是将结果投影到一个新的匿名类型中,Key 是 PropA 或 PropB 的值,Values 是所有匹配元素的 IEnumerable

编辑:代码演练

前两行 linq 行使用给定的键从枚举中进行查找(实际上是一个多值字典)。这些可以枚举为 IEnumerable>,但也可以用于高效查找。

var lookupA = list.ToLookup(e => e.PropA);
var lookupB = list.ToLookup(e => e.PropB);

下一行是查找 PropA 和 PropB 的所有不同值(使用查找,但也可以为此返回列表)。

var keys = lookupA.Select(e => e.Key).Concat(lookupB.Select(e => e.Key)).Distinct();

最后一行采用不同的键,从 propA 查找和 propB 查找中获取匹配的枚举,然后将它们连接起来,并且(现在我发现了另一个错误)对它们进行去重。

select 语句正在生成匿名类型 - 这些类型不能明确引用,但可以分配给 var 并可以枚举它们。如果要存储结果值,则必须创建非匿名类型。

var result = keys.Select(e => 
    new 
    { 
        Key = e, 
        Values = lookupA[e].Concat(lookupB[e]).Distinct()
    });

编辑:输出

CA
    Values: (PropA: CA, PropB: NY, SomeNumber: 1) (PropA: CA, PropB: OR, SomeNumber: 2)
NY
    Values: (PropA: NY, PropB: OR, SomeNumber: 5) (PropA: CA, PropB: NY, SomeNumber: 1)
OR
    Values: (PropA: CA, PropB: OR, SomeNumber: 2) (PropA: NY, PropB: OR, SomeNumber: 5)