LINQ-groupBy,包含多个Group中的项
本文关键字:Group 包含多 LINQ-groupBy | 更新日期: 2023-09-27 18:25:59
我有一个对象列表,它有两个属性,比如a
和b
。a
是类似枚举的:
[Flags] enum MyEnum
{
first = 1,
second = 2,
third = 4,
fourth = 8
};
b
是一个无符号整数,它是掩码(MyEnum标志的组合)。
现在我需要用它们的a
求和每个对象——这意味着如果obj.a = first | third
和我似乎不能对它们做groupBy
,那么一个对象可以求和两次。有不同的方法来求和吗?
很抱歉我没有分享我的代码,但我不能。我可以告诉你,我只是在一些if-else块中使用foreach,但我认为我应该学习如何在Linq 中做到这一点
编辑:我想我不清楚。我想通过枚举对对象求和,这意味着如果我有:
obj1.a = first, obj1.b = 5
obj2.a = first | second, obj2.b = 3
那么输出将是
first sum = 8
second sum = 3
给定MyEnum
和MyClass
[Flags]
enum MyEnum
{
first = 1,
second = 2,
third = 4,
forth = 8
}
class MyClass
{
public MyEnum MyEnum;
public uint Value;
}
和一些值
var mcs = new[] {
new MyClass { MyEnum = MyEnum.first | MyEnum.third, Value = 10 },
new MyClass { MyEnum = MyEnum.second, Value = 20 },
new MyClass { MyEnum = MyEnum.first, Value = 100 },
};
此LINQ表达式将为枚举的每个值返回值的总和。
var ret = from p in Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()
select new { MyEnum = p, Sum = mcs.Where(q => q.MyEnum.HasFlag(p)).Sum(q => q.Value) };
请注意,即使对于值为0
的MyEnum.fourth
,它也会返回一个"行"。
该表达式从枚举的值(Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()
)开始,然后对每个值求和具有相同MyEnum
(mcs.Where(q => q.MyEnum.HasFlag(p)).Sum(q => q.Value)
)的mcs
的值
如果要排除未使用的枚举值:
var ret = from p in Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()
let temp = mcs.Where(q => q.MyEnum.HasFlag(p)).Select(q => q.Value).ToArray()
where temp.Length > 0
select new { MyEnum = p, Sum = temp.Sum(q => q) };
该表达式从枚举的值(Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()
)开始,然后为每个值"保存"temp
中具有相同MyEnum
(let temp = mcs.Where(q => q.MyEnum.HasFlag(p)).Select(q => q.Value).ToArray()
)的mcs
的值,跳过空的temp
(where temp.Length > 0
)并求和剩余的temp
(select new { MyEnum = p, Sum = temp.Sum(q => q) }
)。请注意,如果使用uint
,则必须使用temp.Sum(q => q)
,但使用int
,则可以使用temp.Sum()
(或使用temp.Sum(q => q)
)。
另一种方式是通过双from
和group by
var ret = from p in Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()
from q in mcs
where q.MyEnum.HasFlag(p)
group q.Value by p into r
select new { MyEnum = r.Key, Sum = r.Sum(p => p) };
这可能相当于使用ChaseMedallion建议的SelectMany
(编译器将双from
转换为SelectMany
)
尝试使用HasFlag和GetValues提取特定枚举值的所有标志:
var objs = new[] { new { a = MyEnum.first | MyEnum.second }, new { a = MyEnum.first }... };
var enumValues = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().ToArray();
// first, use SelectMany to create a sequence with one instance of each object for
// each flag value in it's a
var grouped = objs.SelectMany(
o => enumValues.Where(flag => o.a.HasFlag(flag))
.Select(v => new { o, flag })
)
// then group by the extracted flag value
// the element selector (t => t.o) gets us back to an IGrouping of o's (dropping the extra flag property
.GroupBy(t => t.flag, t => t.o);
// finally, we can get sums by a as follows:
var sumsByA = grouped.ToDictionary(g => g.Key, g => g.Sum(o => o.b));