Linq extension method, how to find child in collection recursive

I'm already familiar with Linq but have little understanding of extension methods I'm hoping someone can help me out.

So I have this hierarchical collection pseudo code ie:

class Product
  prop name
  prop type
  prop id
  prop List<Product> children

And I have a list of products List products.

Is there any way I can look for product in this collection by the id with a extension method ? In other words I need one item somewhere within the hierarchy.

11
задан Mike Marks 8 October 2013 в 19:47
поделиться

4 ответа

Вот общее решение, которое сокращает обход иерархии после нахождения совпадения.

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);
17
ответ дан 3 December 2019 в 04:12
поделиться

Вы можете сгладить древовидную структуру, используя этот метод расширения:

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];
8
ответ дан 3 December 2019 в 04:12
поделиться

Если вы хотите выполнить повторную итерацию и найти дочерний элемент в списке продуктов:

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);
-1
ответ дан 3 December 2019 в 04:12
поделиться
static IEnumerable<Product> FindProductById(this IEnumerable<Product> source, int id) 
{
    return source.FirstOrDefault(product => product.Id = id) ?? source.SelectMany(product => product.Children).FindProductById(id);
}
0
ответ дан 3 December 2019 в 04:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: