用于搜索集合的Lambda表达式(或其他方式)

时间:2017-03-20 18:49:16

标签: c# lambda

我有一个包含行星及其卫星作为儿童系列的集合 它是一个集合,但它确实代表了树状结构。为简单起见,我只显示了2个树级别,但每个行星或月球都可以进一步拥有化学元素的集合,所以为了简单起见,我只使用2级树。

Mercury

Venus

Mars
  - Deimos
  - Phobos

Jupiter
  - Europa
  - Ganymede
  - Io

我知道如何将此集合转换为列表,我只需使用

var myList = myCollection.Values.ToList();

我想在此列表中搜索名称中包含“m”的每个项目。如果父母的名字中没有“m”,但是它的任何子卫星都有,我想包括那个孩子(月亮)和它的父母(星球)。在木星的情况下,我会在我的列表中包括木星和木卫三。

我对“m”的搜索将返回以下列表

{Mercury,Mars,Deimos,Jupiter,Ganymede}

我更喜欢使用lambda,但不需要这样做

更新:结构

BodyNode
-ID      [Guid]
-Name    [string]
-IsChild [bool]
-Parent  [BodyNode]
-Children[BodyList ObservableCollection of BodyNode]

BodyTreeNode : BodyNode
-Expanded   [bool]
-Selected   [bool]
-Enabled    [bool]

4 个答案:

答案 0 :(得分:2)

如果您确定您的数据是树状结构,那么您可以执行类似的操作(不检查周期):

 bool GetNodes(Tree root, List<Tree> result, Func<Tree, bool> f) {
     bool add = f(root);
     foreach (var child in root.Children) {
         add ||= GetNodes(child, result, f);
     }
     if (add)
          result.Add(root);
     return add;
}

f是让您知道是否添加树的功能。例如。 (t)=>t.Name.Contains("m")

编辑:假设所有对象派生自Base,Base具有属性public List<Base> GetChildren{get;},则上述逻辑可以实现为:

 bool GetNodes(Base b, List<Base> result, Func<Base, bool> f) {
     bool add = f(b);
     foreach (var child in b.GetChildren) {
          add ||= GetNodes(child);
     }
     if (add) result.Add(b);
     return add;
}

然后您将其用作:

var r = new List<Base>();
myList.Foreach(o => GetNodes(o, r, (b) => b.Name.Contains("m")); 

答案 1 :(得分:2)

假设您有一系列集合的所有元素。然后,您可以在该序列上使用Where或任何其他序列运算符。因此,最好的办法是建立这个序列:

static class Extensions {
 public static IEnumerable<Nodes> Flatten(this IEnumerable<Node> nodes)
 {
  foreach(var node in nodes)
  {
    yield return node;
    foreach (var child in node.Children.Flatten())
      yield return child;
  }
 }
}

容易腻。现在你可以简单地说:

var results = from node in myCollection.Values.Flatten()
              where node.Name.ToLower().Contains("m")   
              select node;

这是名称中包含m的每个元素的列表:

或使用lambda

var results = myCollection.Values.Flatten()
  .Where(node => ... and so on ... );

现在,您想要的是名称中包含m或任何子项的每个元素的列表。所以写下来;我们拥有所需的所有工具!

var results = from node in myCollection.Values.Flatten()
              where node.Name.Contains("m") || node.Children.Flatten().Any(node.Name.Contains("m"))
              select node;

现在,这效率很低 - 你能明白为什么吗? - 但它可以解决问题。现在你有一些可以分析的工作,并且如果你真的需要的话,可以提高效率。

答案 2 :(得分:1)

var MRecords = myList.Where(x=>x.toUpper().Contains("M"));

var result = new HashSet<"yourClass">(); //i didn`t use hashset before but this as stated in [here](http://stackoverflow.com/questions/6391738/what-is-the-difference-between-hashsett-and-listt) eliminates duplicates 

foreach(var record in MRecords)
{
    result.Add(record);
    var ParentLooper = record; 
    while(ParentLooper.parent!=null) //i suppose roots have the parent as null
    {
         result.add(ParentLooper.parent);
         ParentLooper = ParentLooper.parent;
    }
}
return result;

答案 3 :(得分:0)

就个人而言,我建议在处理这样的树实体时使用包含objects个自定义类的IEnumerables。看起来你的Planet和你的月亮都具有相同的属性,所以它们基本上都属于Planet类。鉴于我们只处理名称,我创建了一个私有模型类,如下所示:

private class Planet
        {
            public string Name { get; set; } = string.Empty;
            public IEnumerable<Planet> Children { get; set; } = new List<Planet>();
        }

这使得Lambdas的使用更简单一些。我没有测试这个,但是如果它不正确它接近它并且应该让你到那里:

      IEnumerable<Planet> myCollection = new List<Planet>();
      myCollection = LetThereBeLight(true); //method to populate the list
      var myList = myCollection.ToList().Where(t => t.Name.ToUpper().Contains("M"));
      myList.ToList().AddRange
                            (
                             myCollection.SelectMany(t => t.Children.Where
                                    (
                                      v => v.Name.ToUpper().Contains("M")).Distinct()
                                    )
                            );

你可以把它全部变成一个Lambda,但是如果不把它全部压成一行就很难阅读。这将为您提供一个IEnumerable<Planet>如果您想表示卫星的差异,例如在名称string中添加“ - ”,则可以遍历每个Planet's {{1收集。您可能希望确保仅使用Children,因为此处完全可以复制。