我已经熟悉Linq,但对扩展方法知之甚少,我希望有人可以帮助我。
所以我有这个分层集合伪代码,即:
class Product
prop name
prop type
prop id
prop List<Product> children
我有一份产品列表产品清单。
有什么方法可以通过 id 使用扩展方法在此集合中查找产品?换句话说,我需要在层次结构中的某个位置使用一个项目。
答案 0 :(得分:17)
这是一个通用的解决方案,一旦找到匹配项,就会短路遍历层次结构。
public static class MyExtensions
{
public static T FirstOrDefaultFromMany<T>(
this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector,
Predicate<T> condition)
{
// return default if no items
if(source == null || !source.Any()) return default(T);
// return result if found and stop traversing hierarchy
var attempt = source.FirstOrDefault(t => condition(t));
if(!Equals(attempt,default(T))) return attempt;
// recursively call this function on lower levels of the
// hierarchy until a match is found or the hierarchy is exhausted
return source.SelectMany(childrenSelector)
.FirstOrDefaultFromMany(childrenSelector, condition);
}
}
在你的情况下使用它:
var matchingProduct = products.FirstOrDefaultFromMany(p => p.children, p => p.Id == 27);
答案 1 :(得分:8)
您可以使用以下扩展方法展平树形结构:
static IEnumerable<Product> Flatten(this IEnumerable<Product> source)
{
return source.Concat(source.SelectMany(p => p.Children.Flatten()));
}
用法:
var product42 = products.Flatten().Single(p => p.Id == 42);
请注意,这可能不是很快。如果您反复需要按ID查找产品,请创建字典:
var dict = products.Flatten().ToDictionary(p => p.Id);
var product42 = dict[42];
答案 2 :(得分:1)
我只是重构dtb的解决方案,使其更通用。试试这个扩展方法:
public static IEnumerable<T> Flatten<T, R>(this IEnumerable<T> source, Func<T, R> recursion) where R : IEnumerable<T>
{
return source.SelectMany(x => (recursion(x) != null && recursion(x).Any()) ? recursion(x).Flatten(recursion) : null)
.Where(x => x != null);
}
你可以像这样使用它:
productList.Flatten(x => x.Children).Where(x => x.ID == id);
答案 3 :(得分:0)
static IEnumerable<Product> FindProductById(this IEnumerable<Product> source, int id)
{
return source.FirstOrDefault(product => product.Id = id) ?? source.SelectMany(product => product.Children).FindProductById(id);
}
答案 4 :(得分:0)
使用yield来优化所需枚举的替代解决方案。
public static IEnumerable<T> SelectManyRecursive<T>(
this IEnumerable<T> source,
Func<T, IEnumerable<T>> childrenSelector)
{
if (source == null)
throw new ArgumentNullException("source");
foreach (var i in source)
{
yield return i;
var children = childrenSelector(i);
if (children != null)
{
foreach (var child in SelectManyRecursive(children, childrenSelector))
{
yield return child;
}
}
}
}
然后你可以通过调用FirstOrDefault:
之类的东西找到匹配 var match = People.SelectManyRecursive(c => c.Children)
.FirstOrDefault(x => x.Id == 5);
答案 5 :(得分:-1)
如果您想“进行子迭代”并在产品列表中找到一个孩子:
List<Product>
Product
Child
Child
Child
Child
Product
Child
Child *find this one
Child
您可以使用现有的SelectMany
扩展方法。 SelectMany可用于“展平”两级层次结构。
以下是SelectMany的一个很好的解释:http://team.interknowlogy.com/blogs/danhanan/archive/2008/10/10/use-linq-s-selectmany-method-to-quot-flatten-quot-collections.aspx
你的语法是这样的:
List<Product> p = GetProducts(); //Get a list of products
var child = from c in p.SelectMany(p => p.Children).Where(c => c.Id == yourID);