【问题标题】:Print a list of objects with lists inside lists打印列表中包含列表的对象列表
【发布时间】:2016-07-05 20:00:29
【问题描述】:

我正在尝试打印带有子项的列表,所有子项都以数字分隔,例如:

1 项 1 2 项目 2 2.1 项目 2 子 1 2.2 项目 2 子 2 2.2.1 项目 2 子 2 子 1 3 最后

即:

  1. 项目编号从 1 开始。
  2. 项目应在打印前带有“行标题”,以显示其项目编号。
  3. 当列表中的项目本身是一个列表时,其元素将使用前一个项目的行标题打印,并附加当前列表的每个元素的项目编号。 IE。标题看起来像#.#...#.X,其中#.#...# 表示由该规则构造的上一个项目的行标题,X 是当前列表中当前元素的项目编号。
  4. 如果列表作为列表中的第一项出现,因此没有“前项”,则假定前项是为了生成“前一项的行标题”,即行标题将包括1 的索引值,即使之前没有具有该索引的项目。

我几乎可以使用这段代码:

 var input = new List<object>
        {
             "item 1",
             "item 2",
             "item 3",
             new List<object>{
                 "level 2, item 1",
                 "level 2, item 2",
                 new List<object>{
                     "level 3, item 1",
                      new List<object>{
                         "level 4, item 1",
                         "level 4, item 2"
                      },
                     "level 3, item 2",
                 },
                 "level 2, item 3",
             },
             "last item"
        };


public static string Solve(List<object> list, int level = 0, int subLevel = 0)
{
    var result = new StringBuilder();
    if (level == subLevel)
    {
        level = 0;
    }

    list.ForEach(x =>
    {
        var type = x.GetType();

        if (subLevel != 0)
        {
            result.Append(subLevel.ToString() + ".");
            Console.Write(subLevel.ToString() + ".");
        }

        if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)))
        {
            var newLevelList = x as List<object>;
            result.Append(Solve(newLevelList, level, level));
        }
        else
        {
            level = level + 1;
            result.AppendLine(level.ToString() + " " + x.ToString());
            Console.WriteLine(level.ToString() + " " + x.ToString());
        }
    });

    return result.ToString();
}

但它不适用于第 4 级子项,例如以下示例:

我得到这个输出:

1 项 1 2 项目 2 3 项目 3 3.1 2级,第1项 3.2 2级,第2项 3.2.1 3级,第1项 2.1.1 4级,第1项 1.2 4级,第2项 2.2 3级,第2项 3.3 2级,第3项 4 最后一项

我错过了什么?我也愿意接受新的解决方案。

【问题讨论】:

  • 不要使用List&lt;T&gt; 和使用List&lt;T&gt;.ForEach 使用IEnumerable 和普通foreach(我怀疑它会解决你的问题)
  • 看起来你没有增加 subLevel 参数,而是使用 level 参数。行内:result.Append(Solve(newLevelList, level, level));
  • 请提供一个良好的minimal reproducible example 以可靠地重现问题。在给出您描述的 exact 输入的情况下,还请提供您希望获得的 exact 输出的清晰、准确的描述。根据您声称使用的输入和您显示的代码,我看不出您如何获得像 "item 1" 这样的输出。我当然可以想象一个更简单的实现来实现您似乎想要的东西,但是如果没有更精确的问题,就不清楚您在寻找什么答案。
  • 您不能检查typeof(List&lt;&gt;),然后强制转换为List&lt;object&gt;,因为该列表包含任何内容,而不仅仅是object

标签: c# list


【解决方案1】:

如果没有更多细节,就很难准确理解您要完成的工作。但是,根据您给出的示例,在我看来,当算法找到子列表时,您希望将当前项目索引保留在行标题中,并将新级别的索引从 1 开始附加到那个标题。 IE。如果您刚刚显示了项目"2 item 2",并且随后有一个列表,则列表中的第一个项目应显示为"2.1 Item 2 sub 1",而不是例如"3.1 Item 2 sub 1"(即列表本身不算作该级别的新索引)。

如果我理解正确,那么这样的事情应该做你想做的事:

static string Solve(List<object> source, string prefix = "")
{
    int itemIndex = 0;
    StringBuilder result = new StringBuilder();

    foreach (object item in source)
    {
        List<object> listItem = item as List<object>;

        if (listItem != null)
        {
            result.Append(Solve(listItem, prefix + itemIndex + "."));
        }
        else
        {
            itemIndex++;
            result.AppendLine(prefix + itemIndex + " " + item);
        }
    }

    return result.ToString();
}

请注意,我不是将索引传递给方法,而是简单地传递行的标题前缀,而不是使用显式反射来识别列表中的 List&lt;object&gt; 元素,我只是使用 C# as 运算符来制作对类型的条件转换。

根据您的评论,虽然您希望列表项采用前一项的索引,但您仍然希望容纳没有前一项的列表项。恕我直言,这有点奇怪,但通过解决特殊情况很容易解决,如下所示:

static string Solve(List<object> source, string prefix = "")
{
    int itemIndex = 0;
    StringBuilder result = new StringBuilder();

    foreach (object item in source)
    {
        List<object> listItem = item as List<object>;

        if (listItem != null)
        {
            // If there was no preceding item, pretend there was one
            if (itemIndex == 0)
            {
                itemIndex = 1;
            }

            result.Append(Solve(listItem, prefix + itemIndex + "."));
        }
        else
        {
            itemIndex++;
            result.AppendLine(prefix + itemIndex + " " + item);
        }
    }

    return result.ToString();
}

如果上述内容不能解决您的问题,请改进问题,以便更清楚地准确描述您想要针对某些特定输入的输出。

【讨论】:

  • 这看起来不错,但我发现这个输入有问题: var list2 = new List { "level 1", "level 1", new List{ "level 2 ", "level 2", new List{ new List{ "level 4", }, "level 3, item 2" }, "level 2, item 3" }, "last item" };
  • 看到一个列表直接有另一个列表,之前没有项目,所以我得到输出:2.2.0.1, I can't have that ZERO there
  • 递归的简洁应用:)
猜你喜欢
  • 2016-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-24
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2016-10-01
相关资源
最近更新 更多