LINQ - groupBy with items in several Group

c#, linq

Solution

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`)

Problem

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 ```

Original source