【问题标题】:Is that a faster way to convert hierarchy list to a list based on how many path it cloud have?这是基于云有多少路径将层次结构列表转换为列表的更快方法吗?
【发布时间】:2017-02-18 08:58:54
【问题描述】:

我不知道这个问题的正确命名是什么。

定义很简单,就是一个层次列表(我也可以生成扁平列表)

Simply show hierarchy view
hierarchy I able to get
1
-->2
-->3
   -->4
   -->5
-->6

flattened list I able to get
[1, 2, 3, 4, 5, 6]

我想要的结果是

[
  [1,2],
  [1,3,4],
  [1,3,5],
  [1,6],
]

我的示例课程

public class MemberNetworkViewModel
{
    public int MemberGenerationNumber { get; set; }
    public int MemberId { get; set; }
    public int ParentId{ get; set; }
    public List<MemberNetworkViewModel> children { get; set; }
}

我可以做最难的方法是尝试获取此层次结构列表中的最后一个节点,然后foeeach 他们并一一获取其父节点。但我认为会有更好的方法,有什么想法吗?

当前的解决方案(绕过,我知道很乱,也许通过 linq 寻求更短的帮助?)

    public List<List<MemberNetworkViewModel>> GetAllPossibleNetworkTreePath(
        List<MemberNetworkViewModel> flatternMemberNetworkViewModel)
    {
        var possibleTreePaths = new List<List<MemberNetworkViewModel>>();

        var lastNodes =
            flatternMemberNetworkViewModel.Where(
                x => flatternMemberNetworkViewModel.All(y => y.ParentId!= x.MemberId));

        foreach (var lastNode in lastNodes)
        {
            var memberNetworkViewModels = new List<MemberNetworkViewModel>();
            memberNetworkViewModels.Add(lastNode);
            for (int index = 0; index < lastNode.MemberGenerationNumber; index++)
            {
                var parent =
                    flatternMemberNetworkViewModel.FirstOrDefault(
                        x => x.MemberId == memberNetworkViewModels.Last().ParentId);
                memberNetworkViewModels.Add(parent);
            }
            memberNetworkViewModels = (from x in memberNetworkViewModels
                orderby x.MemberGenerationNumber
                select x).ToList();

            possibleTreePaths.Add(memberNetworkViewModels);
        }

        return possibleTreePaths;
    }

【问题讨论】:

  • 看起来很复杂......我会用树来代替
  • 那么你的子列表就是从root到leaf的路径,需要的话可以很容易的获取到
  • @CedricDruck 因为我需要根据树路径进行一些检查。我知道听起来很好奇,但这是客户要求的。所以我认为我自己的建议是更好的方法?
  • 我不确定我是否理解这个问题。你能在代码中显示源列表,并在代码中显示所需的列表吗?
  • 为什么是1,2,3,4 而不是1,3,4 ???还是那个错字

标签: c# hierarchy


【解决方案1】:

如果您将源数据保存在树中,这将变得容易得多。

看起来你已经有一棵树,以列表的形式保存。您只需要知道列表中代表根的元素,并将其作为遍历树的起点。

您可以编写一个通用的方法来遍历树并输出到所有叶子的路径,如下所示:

public static void Flatten<T, U>(T root, Func<T, U> select, Func<T, IEnumerable<T>> children, Action<List<U>> output)
{
    List<U> pathSoFar = new List<U>();
    flatten(pathSoFar, root, select, children, output);
}

static void flatten<T, U>(List<U> pathSoFar, T root, Func<T, U> select, Func<T, IEnumerable<T>> children, Action<List<U>> output)
{
    pathSoFar.Add(select(root));
    bool any = false;
    var offspring = children(root);

    if (offspring != null)
    {
        foreach (var child in offspring)
        {
            any = true;
            flatten(pathSoFar, child, select, children, output);
        }
    }

    if (!any)
        output(pathSoFar.ToList());

    pathSoFar.RemoveAt(pathSoFar.Count-1);
}

您作为output 参数传递的Func 将为每个叶路径调用一次。

复制您的输入的完整可编译控制台应用程序如下:

using System;
using System.Linq;
using System.Collections.Generic;

namespace ConsoleApp3
{
    class Node
    {
        public int ID;
        public List<Node> Children;
    }

    public class MemberNetworkViewModel
    {
        public int MemberGenerationNumber { get; set; }
        public int MemberId { get; set; }
        public int ParentId { get; set; }
        public List<MemberNetworkViewModel> children { get; set; }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Node root =
                new Node {ID = 1, Children = new List<Node>{
                    new Node {ID = 2 },
                    new Node {ID = 3, Children = new List<Node>{
                        new Node {ID = 4},
                        new Node {ID = 5}}},
                    new Node {ID = 6}
                }};

            Flatten(
                root, 
                node => node.ID,
                node => node.Children,
                path => Console.WriteLine(string.Join(", ", path)));
        }

        public static void Flatten<T, U>(T root, Func<T, U> select, Func<T, IEnumerable<T>> children, Action<List<U>> output)
        {
            List<U> pathSoFar = new List<U>();
            flatten(pathSoFar, root, select, children, output);
        }

        static void flatten<T, U>(List<U> pathSoFar, T root, Func<T, U> select, Func<T, IEnumerable<T>> children, Action<List<U>> output)
        {
            pathSoFar.Add(select(root));
            bool any = false;
            var offspring = children(root);

            if (offspring != null)
            {
                foreach (var child in offspring)
                {
                    any = true;
                    flatten(pathSoFar, child, select, children, output);
                }
            }

            if (!any)
                output(pathSoFar.ToList());

            pathSoFar.RemoveAt(pathSoFar.Count-1);
        }
    }
}

对于您的 MemberNetworkViewModel 课程,您可以这样称呼它:

MemberNetworkViewModel root = whatever;

Flatten(
    root,
    node => node.MemberId,
    node => node.children,
    path => Console.WriteLine(string.Join(", ", path)));

【讨论】:

  • 看起来很好,但为什么是pathSoFar.ToList()?通过删除您在递归中添加的元素,可以轻松避免大量的列表(和底层数组)复制。
  • @KrisVandermotten 这是真的;我只是为了简单。我已经修改了代码以防止制作如此多的副本 - 但请注意,我仍然为每个返回的列表制作一个副本(必须这样做,否则返回的列表会在迭代过程中不断变化!)
  • @MatthewWatson 这太令人惊讶了!看来我要在Func, Action and generic 上学习更多
猜你喜欢
  • 2010-10-20
  • 1970-01-01
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 2016-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多