如何使用Linq从嵌套List中查找特定实体的所有出现 [英] How to find all occurance of a particular entity from nested List using Linq

查看:127
本文介绍了如何使用Linq从嵌套List中查找特定实体的所有出现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个嵌套类如下:

  public   class 
{

public int Id { get ; set ; }

public string 描述{获得; set ; }

public int ParentId {获得; set ; }

public 列表<组>论坛{获取; set ; }

}



我有一个类别组列表,如

 List< ;组> gp =  new 列表< Group>(); 



我的要求是查找列表的所有Id值gp使用Linq.I尝试使用Selectmany(),但它只返回第一层hirarchy。

解决方案

你将不得不在外面写一些递归Linq查询。 Linq不会单独进行递归。

类似于:

  public  静态列表<组>展平(列表<组>组)
{
列表<组> result = new 列表< Group>();
if (groups!= null
{
// 解决自我引用的lambda问题。
// (并且,是的,它必须是两个单独的语句,并且需要进行空初始化)
Action< ;组> recurser = null ;
recurser = g = > {
if (g!= null
{
g.Groups.ForEach(recurser);
result.Add(g);
}
};
groups.ForEach(递归);
}
返回结果;
}



(这是预购,深度优先,遍历。)



现在你可以:

 List< int> AllIds = Flatten( RootGroupList )。选择(g = >  g.Id)。ToList(); 





编辑:

附加说明:这不会检查 Groups 属性不是 null 。确保这一点的一种相对安全的方法是改变:

  public  
{
public int Id { get ; set ; }
public string 描述{ get ; set ; }
public int ParentId { get ; set ; }
private readonly 列表<组> _Groups = new 列表< Group>();
public 列表<组>小组{获取 {返回 _Groups; }
}



但是,这可以防止批发替换 Groups 属性。其内容必须使用 .Clear() .Add()等进行操作。


这是我使用的另一种方法,它不需要将整个层次结构存储在列表中,也不依赖于特定的数据结构:

  private   static  IEnumerable< TResult> SelectRecursiveIterator< TSource,TResult>(IEnumerable< TSource> source,Func< TSource,IEnumerable< TSource>> getChildren,Func< TSource, int ,TResult> selector)
{
var stack = new Stack< IEnumerator< TSource>>() ;

尝试
{
stack.Push(source.GetEnumerator());
while 0 != stack.Count)
{
var iter = stack.Peek();
if (iter.MoveNext())
{
var current = iter.Current;
yield return selector(current,stack.Count - 1 );

var children = getChildren(current);
if (children!= null )stack.Push(children.GetEnumerator());
}
else
{
iter.Dispose();
stack.Pop();
}
}
}
最后
{
while 0 != stack.Count)
{
stack.Pop()。Dispose();
}
}
}

public static IEnumerable< TResult> SelectRecursive< TSource,TResult>( this IEnumerable< TSource> source,Func< TSource,IEnumerable< TSource>> getChildren,Func< TSource, int ,TResult>选择器)
{
if (source == null throw new ArgumentNullException( source);
if (selector == null throw new ArgumentNullException( selector);
if (getChildren == null return source.Select(s = > 选择器(s, 0 ));
return SelectRecursiveIterator(source,getChildren,selector);
}

public static IEnumerable< T> SelectRecursive< T>( IEnumerable< T>来源,Func< T,IEnumerable< T>> getChildren)
{
if (source == null throw new ArgumentNullException( source);
if (getChildren == null return 来源;
return SelectRecursiveIterator(source,getChildren,(s,_)= > s);
}





要获取所有ID列表,您可以使用以下内容:

 List< int> allIds = groups.SelectRecursive(g = >  g.Groups)。选择(g = >  g .ID).ToList(); 


I have a nested class like following:

public class Group
{

    public int Id { get; set; }

    public string Description { get; set; }

    public int ParentId { get; set; }

    public List<Group> Groups { get; set; }

}


I have a List of class Group like

List<Group> gp=new List<Group>();


My requirement is to find all the Id values of list gp using Linq.I tried with Selectmany(), but its returning me only the first level of hirarchy.

解决方案

You're going to have to write recursion somewhere outside of the Linq query. Linq does not do recursion on its own.
Something like:

public static List<Group> Flatten(List<Group> groups)
{
  List<Group> result = new List<Group>();
  if (groups != null)
  {
    // a bit of a hack to get around the self referential lambda issue.
    // (And, Yes, it must be two separate statements, and the null initialization is required)
    Action<Group> recurser = null;
    recurser = g => {
      if (g != null)
      {
        g.Groups.ForEach(recurser);
        result.Add(g);
      }
    };
    groups.ForEach(recurser);
  }
  return result;
}


(This is a pre-order, depth-first, traversal.)

Now you can:

List<int> AllIds = Flatten(RootGroupList).Select(g => g.Id).ToList();



Edit:
An additional note: This doesn't check that the Groups property isn't null. A relatively safe way to ensure this would be to change:

public class Group
{ 
    public int Id { get; set; }
    public string Description { get; set; }
    public int ParentId { get; set; }
    private readonly List<Group> _Groups = new List<Group>();
    public List<Group> Groups { get { return _Groups; } }
}


However, this prevents wholesale replacing the Groups property. Its contents must be manipulated with .Clear(), .Add(), etc.


Here's an alternative approach that I use which doesn't require storing the entire hierarchy in a list, and isn't tied to a particular data structure:

private static IEnumerable<TResult> SelectRecursiveIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> getChildren, Func<TSource, int, TResult> selector)
{
    var stack = new Stack<IEnumerator<TSource>>();

    try
    {
        stack.Push(source.GetEnumerator());
        while (0 != stack.Count)
        {
            var iter = stack.Peek();
            if (iter.MoveNext())
            {
                var current = iter.Current;
                yield return selector(current, stack.Count - 1);

                var children = getChildren(current);
                if (children != null) stack.Push(children.GetEnumerator());
            }
            else
            {
                iter.Dispose();
                stack.Pop();
            }
        }
    }
    finally
    {
        while (0 != stack.Count)
        {
            stack.Pop().Dispose();
        }
    }
}

public static IEnumerable<TResult> SelectRecursive<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> getChildren, Func<TSource, int, TResult> selector)
{
    if (source == null) throw new ArgumentNullException("source");
    if (selector == null) throw new ArgumentNullException("selector");
    if (getChildren == null) return source.Select(s => selector(s, 0));
    return SelectRecursiveIterator(source, getChildren, selector);
}

public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getChildren)
{
    if (source == null) throw new ArgumentNullException("source");
    if (getChildren == null) return source;
    return SelectRecursiveIterator(source, getChildren, (s, _) => s);
}



To get the list of all IDs, you would then use something like this:

List<int> allIds = groups.SelectRecursive(g => g.Groups).Select(g => g.Id).ToList();


这篇关于如何使用Linq从嵌套List中查找特定实体的所有出现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆