C
C#2y ago
Becquerel

Grouping an IEnumerable based on multiple fields of the target type

I have an object Foo like this:
public class Foo
{
public bool CanA;
public bool CanB;
public bool CanC;
public bool CanD;
}
public class Foo
{
public bool CanA;
public bool CanB;
public bool CanC;
public bool CanD;
}
Given a List<Foo>, I'd like to group them into some kind of collection based on whether these fields are true or not. An item would appear in multiple lists if multiple of these are true. I am currently thinking along these lines:
var dictionary = new Dictionary<string, List<Foo>>();

dictionary.Add("Can A", mySource.Where(x => x.CanA).ToList());
// repeat for B, C, D
var dictionary = new Dictionary<string, List<Foo>>();

dictionary.Add("Can A", mySource.Where(x => x.CanA).ToList());
// repeat for B, C, D
It feels like I should be able to do this more neatly with GroupBy or something, but I've only used that with single values before. Anyone done this?
1 Reply
undisputed world champions
maybe something like this?
IEnumerable<(string Key, Foo Foo)> Generate(Foo f)
{
if (f.CanA)
yield return ("Can A", f);
if (f.CanB)
yield return ("Can B", f);
if (f.CanC)
yield return ("Can C", f);
if (f.CanD)
yield return ("Can D", f);
}

var grouped = foos
.SelectMany(Generate)
.GroupBy(x => x.Key, x => x.Foo);
IEnumerable<(string Key, Foo Foo)> Generate(Foo f)
{
if (f.CanA)
yield return ("Can A", f);
if (f.CanB)
yield return ("Can B", f);
if (f.CanC)
yield return ("Can C", f);
if (f.CanD)
yield return ("Can D", f);
}

var grouped = foos
.SelectMany(Generate)
.GroupBy(x => x.Key, x => x.Foo);
the Generate(Foo f) function could be a method of the class Foo too not as pretty as i would like, though :/ alternatively add a function returning all keys to Foo:
public IEnumerable<string> AllKeys()
{
if (CanA)
yield return "Can A";
if (CanB)
yield return "Can B";
if (CanC)
yield return "Can C";
if (CanD)
yield return "Can D";
}
public IEnumerable<string> AllKeys()
{
if (CanA)
yield return "Can A";
if (CanB)
yield return "Can B";
if (CanC)
yield return "Can C";
if (CanD)
yield return "Can D";
}
and group like this:
var grouped = foos
.SelectMany(f => f.AllKeys().Select(k => (k, f)))
.GroupBy(x => x.k, x => x.f);
var grouped = foos
.SelectMany(f => f.AllKeys().Select(k => (k, f)))
.GroupBy(x => x.k, x => x.f);