Reputation: 54242
I have a data structure where Modules contain Units and Units contain Sections, and from a list of modules, I want to find the first module that contains at least one unit that contains at least one section, and I want to do something with the module, unit and section.
I initially tried to use modules.Find()
for this, but it only tells me what the first non-empty Module is, so I'd have to lookup the Unit twice:
var module = modules.Find(m => m.Units.Exists(u => u.Sections.Count > 0));
if (module == null)
{
throw new Exception("there are no non-empty modules");
}
var unit = module.Units.Find(u => u.Sections.Count > 0);
var section = unit.Sections.First();
doSomeStuff(module, unit, section);
I eventually wrote my own function to do this:
private Tuple<Module, Unit, Section> getFirstModuleWithVisibleSection(List<Module> modules)
{
foreach (var module in modules)
{
foreach (var unit in module.Units)
{
var section = unit.Sections.FirstOrDefault();
if (section != null)
{
return new Tuple<Module, Unit, Section>(module, unit, section);
}
}
}
return null;
}
...
var res = getFirstModuleWithVisibleSection(modules);
if (res == null)
{
throw new Exception("no visible modules");
}
var module = res.Item1;
var unit = res.Item2;
var section = res.Item3;
doSomething(module, unit, section);
This is efficient but it's way more verbose than I was hoping for.
I'm more used to OCaml, where I would use List.find_map
, which is like find
, except instead of returning true/false you return null or not-null, and it returns the first not-null. In C# it would look something like this:
var (module, unit, section) =
modules.FindMap(module =>
module.Units.FindMap(unit =>
{
var section = unit.Sections.FirstOrDefault();
if (section == null)
{
return null;
}
return (module, unit, section);
}));
Is there a way to do this in C#?
Upvotes: 1
Views: 122
Reputation: 5944
What about:
var query = from m in modules
from u in m.Units
let s = u.Sections.FirstOrDefault()
where s != null
select new
{
m,
u,
s
};
var item = query.FirstOrDefault();
Upvotes: 4
Reputation: 7091
Certainly not elegant but it may meet the need.
public Module FirstModuleWithAUnitWithASection(IEnumerable<Module> modules)
=> modules.Where(module => module.Units != null)
.Select(module => module.Units.Where(unit => unit.Sections != null)
.Select(unit => unit.Sections.Select(section => module)
.First()).First()).First();
Upvotes: 1