5

I have a list of object that has two properties, let's say a and b. a is an Enum like:

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

b is an unsigned integer, which is a mask (combination of the MyEnum flags).

now i need to sum every object by their a - meaning an object can be summed twice if obj.a = first | third and i can't seem to do a groupBy to them. is there a different way to sum them?

I'm sorry i don't share my code, but i can't. i can tell you i did it using foreach in just some if - else blocks but i think i should learn how to do it in Linq

EDIT: I think i wasn't clear. i want to sum the object by the Enum, meaning 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 
3
  • could you provide the expected result? your question is not really clear to me Commented Aug 2, 2013 at 9:45
  • Your problem is still not clear. What if we have obj3 = first | second | third; What are the sums? Can they be first sum = 15, second sum = 8, third sum = 3, fourth sum = 7, fifth sum = 8, sixth sum = 14? Commented Aug 2, 2013 at 9:59
  • @KingKing exactly. i sum by the enum values, so if an object has in the enum first|second|third and 3 in the value field then 3 will be added to the sum of first, second and third Commented Aug 2, 2013 at 10:03

2 Answers 2

3

Given a 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 values

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 for each value of the enum the sum of the values.

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 "row" even for MyEnum.fourth with value 0.

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

If you want to exclude the values of the enum that aren't 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) }; 

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

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

It's probably equivalent to using the SelectMany as suggested by ChaseMedallion (a double from is converted by the compiler to a SelectMany)

Sign up to request clarification or add additional context in comments.

1 Comment

wow, really thank you. very explained and good for future use too. excellent answer
3

Try using HasFlag and GetValues to extract all the flags for a particular enum 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'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)); 

1 Comment

thank you for your answer, really good and explained, will try is ASAP

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.