在另一个List <t>中的List <t>中搜索值

时间:2017-11-21 07:32:29

标签: c# linq

我的课程类似于此

List<MainCat> AllCat;

public class MainCat
{
    public string id { get; set; }
    public string name { get; set; }
    public List<subcat> subcat { get; set; }
}

public class subcat
{
    public string id { get; set; }
    public string name { get; set; }
    public List<subsubcat> subsubcat { get; set; }
}

public class subsubcat
{
    public string id { get; set; }
    public string name { get; set; }
}

我想通过 id 获取名称, 例如我知道id是 69

我想得到像这样的输出

MainCat.name&gt; subcat.name&gt; subsubcat.name(如果在subsubcat中找到 69

MainCat.name&gt; subcat.name(如果在subcat中找到 69

MainCat.name(如果在MainCat中找到 69

3 个答案:

答案 0 :(得分:1)

如果我已经正确理解了您的要求,那么查询语法就可以解决这个问题:

IEnumerable<string> MyFunc(IEnumerable<MainCat> mainCategories, string idToMatch)
{
  return (from main in mainCategories
          where main.id == idToMatch
          select main.name)
    .Concat(from main in mainCategories
            from sub in main.subcat
            where sub.id == idToMatch
            select string.Format("{0} > {1}", main.name, sub.name))
    .Concat(from main in mainCategories
            from sub in main.subcat
            from subsub in sub.subsubcat
            where subsub.id == idToMatch
            select string.Format("{0} > {1} > {2}", main.name, sub.name, subsub.name));
}

如果您只对第一场比赛感兴趣,可以将其称为

string resultName = MyFunc(AllCat, "69").FirstOrDefault();

因为查询使用延迟执行,所以如果在主类别中找到匹配,这将避免调用更复杂的查询。

也可以将SelectMany函数与函数调用语法一起使用,但是,它更难以遵循,例如以下是我重新编写第二个.Concat(...)调用内容的方式,以便说明:

mainCategories.SelectMany(main => main.subcat, (main, sub) => new { Main = main, Sub = sub })
              .SelectMany(pair => pair.Sub.subsubcat, (pair, subsub) => new { Main = pair.Main, Sub = pair.Sub, SubSub = subsub})
              .Where(triplet => triplet.SubSub.id == idToMatch)
              .Select(triplet => string.Format("{0} > {1} > {2}", triplet.Main, triplet.Sub, triplet.SubSub));

据我所知,查询语法在幕后编译成与此类似的内容。

<小时/> 接受回答后更新,我回来再次查看我的代码:

另一种可能性是为所有3个类添加一个接口(或者将它们统一到一个类中,或者根据实际用例从公共基类派生)。

这允许一个可以搜索到任意深度的递归实现(下面是两个不同的基于Linq的实现,具体取决于你是否有一个或其他语法的偏好):

public interface ITreeCat
{
  string id { get; }
  string name { get; }
  IEnumerable<ITreeCat> subcat { get; }
}

// add explicit interface implemetantion to existing 3 classes
// e.g.
// IEnumerable<ITreeCat> ITreeCat.subcat { get { return subsubcat; } }
// IEnumerable<ITreeCat> ITreeCat.subcat { get { return Enumerable.Empty<ITreeCat>(); } }

IEnumerable<string> MyFunc(IEnumerable<ITreeCat> categories, string idToMatch, string prefix = "")
{
  return (from cat in categories
          where cat.id == idToMatch
          select prefix + cat.name)
    .Concat(from cat in categories
            from recursiveResult in MyFunc(cat.subcat, idToMatch, prefix + cat.name + " > ")
            select recursiveResult);
}

IEnumerable<string> MyFunc2(IEnumerable<ITreeCat> categories, string idToMatch, string prefix = "")
{
  return categories.Where(cat => cat.id == idToMatch)
                   .Select(cat => prefix + cat.name)
                   .Concat(categories.SelectMany(cat => MyFunc2(cat.subcat, idToMatch, prefix + cat.name + " > ")));
}

这样做的好处是,如果稍后添加subsubsubcat等,它将继续工作。

以上所有代码示例都使用广度优先搜索,并且每次深入一级时都会重复枚举“父级”类别。

在某些应用程序中,深度优先搜索可能是更好的选择,因为每个列表仅枚举一次,在这种情况下,使用foreach而不是Linq更容易。同样,递归版本比具有不同类的3个嵌套循环更简洁:

IEnumerable<string> MyFuncDepthFirst(IEnumerable<ITreeCat> categories, string idToMatch)
{
  foreach(var cat in categories)
  {
    if (cat.id == idToMatch)
      yield return cat.name;
    foreach (var subResult in MyFuncDepthFirst(cat.subcat, idToMatch))
      yield return string.Format("{0} > {1}", cat.name, subResult);
  }
}

这仍假设可能发生多次匹配。如果我们刚刚在第一个匹配之后,则根本不需要使用迭代器块,并且可以修改上述函数以返回一个简单的字符串:

string FirstMatchingIdDepthFirst(IEnumerable<ITreeCat> categories, string idToMatch)
{
  foreach(var cat in categories)
  {
    if (cat.id == idToMatch)
      return cat.name;
    string subResult = FirstMatchingIdDepthFirst(cat.subcat, idToMatch);
    if(subResult != null)
      return string.Format("{0} > {1}", cat.name, subResult);
  }
  return null;
}

答案 1 :(得分:0)

var list = this.AllCat.Where(t=>t.subcat.Any(s=> subsubcat.contains(s));

答案 2 :(得分:0)

您可以选择以下方法

public static Type Find(string id, MainCat m)
{
    if (m.id.Equals(id))
    {
        return m.GetType();
    }
    if (m.subcat.Any(a => a.id.Equals(id)))
    {
        return typeof(subcat);
    }
    if (m.subcat.Any(a => a.subsubcat.Any(b => b.id.Equals(id))))
    {
        return typeof(subsubcat);
    }
    return null;
}

并执行搜索。找到要点,https://gist.github.com/IshamMohamed/33d75064789d77d88404b8ffc9a17e94

通过这种方式,您可以增加内部列表的数量(例如:subsubsubcat