首页 > 解决方案 > 同时查找和映射

问题描述

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

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

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,除了不是返回 true/false 而是返回 null 或 not-null,它返回第一个 not-null。在 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# 中做到这一点?

标签: c#linq

解决方案


关于什么:

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

推荐阅读