代码之家  ›  专栏  ›  技术社区  ›  Brendan Long

同时查找和映射

  •  1
  • Brendan Long  · 技术社区  · 6 年前

    我有一个数据结构,其中模块包含单元,单元包含节,从模块列表中,我想找到第一个模块,其中至少包含一个单元,其中至少包含一个节,我想对模块、单元和节做些什么。

    我最初试着用 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 ,除了不返回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#中做到这一点?

    2 回复  |  直到 6 年前
        1
  •  4
  •   Silvermind    6 年前

    关于:

    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();
    
        2
  •  1
  •   JSteward    6 年前

    当然不高雅,但可以满足需要。

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