同时查找和映射

时间:2018-10-09 22:08:09

标签: c# linq

我有一个数据结构,其中“模块包含单元”和“单元包含节”,并从模块列表中查找第一个包含至少一个单元的模块,该单元包含至少一个节,并且我想做一些事情以及模块,单位和部分。

我最初尝试使用modules.Find(),但是它只能告诉我第一个非空模块是什么,因此我必须两次查找单元:

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

我最终编写了自己的函数来做到这一点:

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

这很有效,但是比我希望的要冗长得多。

我更习惯OCaml,在这里我会使用List.find_map,就像find一样,除了返回null或not-null而不是返回true / false之外,它返回第一个不为零。在C#中看起来像这样:

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

在C#中有没有办法做到这一点?

2 个答案:

答案 0 :(得分:4)

那又怎么样:

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

答案 1 :(得分:1)

肯定不是很优雅,但可以满足需要。

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