【问题标题】:sort a one-dimensional list of items into child/parent structure将一维项目列表排序为子/父结构
【发布时间】:2013-12-27 02:00:43
【问题描述】:

我想知道是否有人可以提供帮助。

我有一个文本文件,其中包含在 \t 上拆分的行。它们有两列,一个代码和一个名称。

我希望将这个一维结构拆分为代码列上的父子层次结构。

数据示例:

 0100000000     Coffee
 0110000000     Mocha
 0120000000     Cappuccino
 0121000000     Semi skimmed
 0121100000     Starbuckz
 0121200000     Costa
 0122000000     Skimmed
 0130000000     Latte

人类可读的层次结构:

 0100000000     Coffee
      0110000000     Mocha
      0120000000     Cappuccino
           0121000000     Semi skimmed
                0121100000     Starbuckz
                0121200000     Costa
           0122000000     Skimmed
      0130000000     Latte

我想将此结构转换为如下格式:

 public class LineData
 {
    public string OriginalCode { get; set; }
    public string Title { get; set; }
    public LineData Parent { get; set; }
    public List<LineData> Children { get; set; }
 }

列表是静态的,我最终可能只是存储在内存中。

【问题讨论】:

  • 到目前为止你有什么想法?另外,数据代表什么层次结构?
  • 我还没有开始,我想知道他们是否是解决这个问题的最佳实践。例如,您是从右到左还是从左到右工作,因为代码用零填充到 8 个字符。
  • 我没有看到父子关系?
  • 我已对其进行了编辑以使其更加可见
  • @Phil 一个递归函数,它采用列表、深度和起点并循环直到该深度处的标志发生变化,这将是一个通用解决方案。

标签: c#


【解决方案1】:

这个怎么样?

var data = " 0100000000     Coffee\r\n 0110000000     Mocha\r\n 0120000000     Cappuccino\r\n 01210" +
    "00000     Semi skimmed\r\n 0121100000     Starbuckz\r\n 0121200000     Costa\r\n 01220" +
    "00000     Skimmed\r\n 0130000000     Latte";
var linesByPrefix = 
    (from l in data.Split(new[]{Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
    let pair = l.Split(new[]{' '},StringSplitOptions.RemoveEmptyEntries)
    select new LineData
    {
        OriginalCode = pair[0],
        Title = pair[1],
        Children = new List<LineData>()
    })
    .ToDictionary(l => l.OriginalCode.TrimEnd('0'));

foreach (var line in linesByPrefix)
{
    var parentCode = line.Key.Substring(0, line.Key.Length - 1);
    LineData parent;
    if(linesByPrefix.TryGetValue(parentCode, out parent))
    {
        line.Value.Parent = parent;
        parent.Children.Add(line.Value);
    }
}
var roots = linesByPrefix.Values.Where(l => l.Parent == null);

【讨论】:

  • 太棒了!非常感谢您在这方面的时间。节日快乐:)
  • 小重构,将 lineData 和 linesByPrefix 替换为 var linesByPrefix = data.Select(d =&gt; d.Split('\t')) .ToDictionary( td =&gt; td.First().TrimEnd('0'), td =&gt; new LineData { OriginalCode = td.First(), Title = td.Last(), Children = new List&lt;LineData&gt;() });
【解决方案2】:

这样的事情可能会起作用:

var lines = File.ReadAllLines(@"...");
Stack<LineData> parents = new Stack<LineData>();
List<LineData> items = new List<LineData>();

foreach (string line in lines) 
{
    string[] parts = Regex.Split(line, @"\s+");

    string code = parts[0];
    string title = parts[1];

    LineData newItem = new LineData 
    { 
        OriginalCode = code,
        Title = title
    };

    LineData parent = null;

    // Find the parent, if any.
    while (parents.Any() && parent == null)
    {
        LineData temp = parents.Peek();

        if (code.Replace("0", string.Empty).Contains(
            temp.OriginalCode.Replace("0", string.Empty)))
        {
            parent = temp;
        }
        else
        {
            parents.Pop();
        }
    }

    if (parent != null)
    {
        parent.Children.Add(newItem);
    }
    else 
    {
        items.Add(newItem);
    }

    parents.Push(newItem);
}

基本上遍历每一行并保留一堆您不断Pop 的祖先,直到找到正确的父。我已将“正确的父级”定义为具有OriginalCode 的祖先,该OriginalCode 包含在当前项目的OriginalCode 中,减去零。

请注意,您还必须为 LineData 添加一个初始化 Children 的构造函数。

【讨论】:

  • 对不起安德鲁 - 小条勇士刚刚打败了你!我花了一些时间测试他的代码,并在您发布前不久将答案授予他。还是谢谢你
  • @Phil:没问题!
  • +1 用于提出性能更好的解决方案。我曾考虑尝试这种基于堆栈的方法,但选择了更面向 LINQ 的解决方案,以避免圈复杂性并支持无序输入。 :-)
  • @StriplingWarrior:谢谢——是的,这确实假设输入的顺序是不利的。我也不喜欢检查“这是父母吗”。好像有点味道。
猜你喜欢
  • 1970-01-01
  • 2019-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-11
  • 1970-01-01
相关资源
最近更新 更多