Перебор списка элементов до их родительского и родительского родительского уровня

#c# #generics #collections

#c# #общие #Коллекции

Вопрос:

У меня есть одно требование для перебора списка элементов, который имеет n-уровень иерархии, и я хотел бы получить восходящие элементы для выбранного идентификатора элемента.

Например, ниже приведены необработанные данные

 ID         ParentID    ItemName       Category
1          -1          Chai           Breweries
4          -1          Mouse-pad      Electronic
3           1          GST            Taxes
2           1          Spices         
5           4          Mobile         
6           3          My Tax         
 

Я хочу запрограммировать на C # для итерации и отображения, например, если я передаю параметр ID для метода 6, тогда он должен выводить выходные данные, как показано ниже

 ParentID=3, Name=My Tax, Category=Taxes
 

Если я передам параметр ID как 2, то вывод должен быть похожим

 ParentID=1, Name=Spices, Category=Breweries
 

пожалуйста, помогите мне в достижении этой функциональности, возможно, с помощью общей коллекции или любого алгоритма

Что я пробовал, так это то, что я пытался использовать List и плюс LINQ’s select many, но с помощью этой опции я смог получить только текущий элемент, но не значение родительской категории, если с текущим элементом не связана категория. Также пытался добавить рекурсивный метод, но не уверен, как построить конечный результат, с рекурсивным мы должны получить только текущий элемент.

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

 class Program
{
    static void Main(string[] args)
    {
        int categoryId = 202;
        var products = GetProducts();
        var product = products.FirstOrDefault(p => p.ID == categoryId);

        var output = GetProductRecursively(products, categoryId, string.Empty);
        Console.WriteLine(output);
        Console.Read();
    }

    public static string GetProductRecursively(List<Product> products, int parentId, string output)
    {
        var product = products.FirstOrDefault(p => p.ParentID == parentId);
        StringBuilder stringBuilder = new StringBuilder();
        if (string.IsNullOrEmpty(product.Category))
        {
            if (string.IsNullOrEmpty(output))
            {
                stringBuilder.Append($"ParentCategoryID={ product.ParentID}, Name={ product.ItemName}, Keywords=");
                GetProductRecursively(products, product.ParentID, stringBuilder.ToString());
            }
            else
                GetProductRecursively(products, product.ParentID, output);
        }
        else
            stringBuilder.Append($"{output}{product.Category}");
        return stringBuilder.ToString();
    }
    public static List<Product> GetProducts()
    {
        var products = new List<Product>();
        products.Add(new Product { ID = 1, ParentID = -1, ItemName = "Chai", Category = "Breweries" });
        products.Add(new Product { ID = 4, ParentID = -1, ItemName = "Mouse-pad", Category= "Electronic" });
        products.Add(new Product { ID = 3, ParentID  = 1, ItemName = "GST", Category= "Taxes" });
        products.Add(new Product { ID = 2, ParentID = 1, ItemName = "Spices" });
        products.Add(new Product { ID = 5, ParentID = 4, ItemName = "Mobile" });
        products.Add(new Product { ID = 6, ParentID = 3, ItemName = "My Tax" });
        return products;
    }
}

public class Product
{
    public int ID { get; set; }
    public int ParentID { get; set; }
    public string ItemName { get; set; }
    public string Category { get; set; }
}
 

Однако на одной итерации он возвращает Category для ParentID, но, поскольку он находится в рекурсии, он продолжает выполнять свою работу для предыдущей итерации, поэтому на данный момент Category все время возвращает «» (empty.string)

Комментарии:

1. Что вы пробовали? Что у вас не работает?

2. Я пытался использовать List<MyOwnClass> и плюс LINQ’s select many , но с помощью этой опции я смог получить только текущий элемент, но не значение родительской категории, если с текущим элементом не связана категория.

3. напишите это в своем вопросе и укажите, что именно пошло не так. если вы ищете однострочный вариант, это не поможет.

4. Я бы рекомендовал вам изучить рекурсивные функции или их альтернативу с некоторыми локальными состояниями и циклами while.

5. выбрать много здесь неприменимо. вам нужно записать свою логику, чтобы найти родительский элемент с помощью category!=null, возможно, с помощью цикла или рекурсивного метода. Полезный совет: «если вы не можете придумать логику без Linq, использование Linq НЕ сделает это возможным».

Ответ №1:

Нерекурсивное решение может выглядеть следующим образом:

 var productId = 6;
var products = GetProducts();

var productDict = products // create dictionary to search for products by id
    .GroupBy(p => p.ID)
    .ToDictionary(p => p.Key, g => g.First());

var product = productDict[productId];

// create loop state variables 
string category = null; 
var currProduct = product;
// cycle while category not found   
while (category == null)
{
    // or there is no parent product
    if (!productDict.ContainsKey(currProduct.ParentID))
    {
        break;
    }

    currProduct = productDict[currProduct.ParentID];
    category = currProduct.Category;
}

Console.WriteLine($"{category}-{product.ItemName}");
 

Ответ №2:

Без какого-либо кода я могу дать вам только ответ «по умолчанию» для вашей проблемы.

Чтобы решить вашу проблему, вы должны реализовать функцию в своих классах, чтобы получить родительский элемент вашего экземпляра.

Чтобы получить абсолютного родителя (у объекта нет самого родителя), вы должны реализовать функцию, которая вызывает себя, пока у нее есть родитель.

Ответ №3:

Ок, наконец, заработал с правильной логикой, рабочий код, как показано ниже

 class Program
{
    static void Main(string[] args)
    {
        int productId = 6;
        var products = GetProducts();
        var product = products.FirstOrDefault(p => p.ID == productId);

        var output = GetProductRecursively(products, productId, string.Empty);
        Console.WriteLine(output);
        Console.Read();
    }

    public static string GetProductRecursively(List<Product> products, int Id, string output)
    {
        var product = products.FirstOrDefault(p => p.ID == Id);
        StringBuilder stringBuilder = new StringBuilder();
        if (string.IsNullOrEmpty(output))
            output = stringBuilder.Append($"ParentCategoryID={ product.ParentID}, Name={ product.ItemName}, Keywords=").ToString();
        if (string.IsNullOrEmpty(product.Category))
        {
            return GetProductRecursively(products, product.ParentID, output);
        }
        else
            output  = $"{product.Category}";
        return output;
    }
    public static List<Product> GetProducts()
    {
        var products = new List<Product>();
        products.Add(new Product { ID = 1, ParentID = -1, ItemName = "Chai", Category = "Breweries" });
        products.Add(new Product { ID = 4, ParentID = -1, ItemName = "Mouse-pad", Category = "Electronic" });
        products.Add(new Product { ID = 3, ParentID = 1, ItemName = "GST", Category = "Taxes" });
        products.Add(new Product { ID = 2, ParentID = 1, ItemName = "Spices" });
        products.Add(new Product { ID = 5, ParentID = 4, ItemName = "Mobile" });
        products.Add(new Product { ID = 6, ParentID = 3, ItemName = "My Tax" });
        return products;
    }
}

public class Product
{
    public int ID { get; set; }
    public int ParentID { get; set; }
    public string ItemName { get; set; }
    public string Category { get; set; }
}