LINQ - groupBy with elements in multiple groups

I have a list of objects that have two properties, say a and b . a - Enum like:

 [Flags] enum MyEnum { first = 1, second = 2, third = 4, fourth = 8 }; 

b is an unsigned integer that is a mask (a combination of MyEnum flags).

Now I need to sum each object by their a - this means that the object can be summed twice if obj.a = first | third obj.a = first | third , and I cannot do groupBy with it. is there any other way to sum them up?

I'm sorry that I do not share my code, but I can not. I can say that I did this using foreach only in some if-else blocks, but I think I should learn how to do it in Linq

EDIT: I think I was not clear. I want to summarize an object using Enum, that is, if I have:

 obj1.a = first, obj1.b = 5 obj2.a = first | second, obj2.b = 3 

then the output will be

 first sum = 8 second sum = 3 
+6
source share
2 answers

Given MyEnum and a MyClass

 [Flags] enum MyEnum { first = 1, second = 2, third = 4, forth = 8 } class MyClass { public MyEnum MyEnum; public uint Value; } 

And some meanings

 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 }, }; 

This LINQ expression will return the sum of the values ​​for each enumeration value.

 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) }; 

Note that it will return a β€œstring” even for MyEnum.fourth with a value of 0 .

The expression begins with enum values ​​( Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>() ), and then for each value it sums the mcs values ​​that have the same MyEnum ( mcs.Where(q => q.MyEnum.HasFlag(p)).Sum(q => q.Value) )

If you want to exclude enumeration values ​​that are not used:

 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) }; 

An expression starts with enum values ​​( Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>() ), and then for each value it "stores" mcs values ​​that have the same MyEnum ( let temp = mcs.Where(q => q.MyEnum.HasFlag(p)).Select(q => q.Value).ToArray() ) in temp , skips temp which are empty ( where temp.Length > 0 ), and summarize the remaining temp ( select new { MyEnum = p, Sum = temp.Sum(q => q) } ). Note: if you use uint , you must use temp.Sum(q => q) , but with int you can use temp.Sum() (or you can use temp.Sum(q => q) ).

Another way is double from and 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) }; 

This is probably equivalent to using SelectMany as suggested by ChaseMedallion (double from converted by the compiler to SelectMany )

+3
source

Try using HasFlag and GetValues ​​to retrieve all the flags for a specific enumeration value:

 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 a var grouped = objs.SelectMany( o => enumValues.Where(flag => oaHasFlag(flag)) .Select(v => new { o, flag }) ) // then group by the extracted flag value // the element selector (t => to) gets us back to an IGrouping of o (dropping the extra flag property .GroupBy(t => t.flag, t => to); // finally, we can get sums by a as follows: var sumsByA = grouped.ToDictionary(g => g.Key, g => g.Sum(o => ob)); 
+3
source

Source: https://habr.com/ru/post/950893/


All Articles