LINQ:向每个第二个位置添加一个项目

8

所以,我有一个列表:

["item1"]
["item2"]
["item3"]

我希望列表看起来像这样:
[""]
["item1"]
[""]
["item2"]
[""]
["item3"]

一个简单的从后往前的循环就能实现这个功能:
for (int i = list.Count-1; i >= 0; i--)
   list.Insert(i, string.Empty);

但我想知道是否有更优雅的方法使用LINQ来完成这个任务?


3
IMO:Linq适合查询,但更新/变异的效果不太好 :) - Jens Kloster
请查看Selman22的答案,那不好吗? :) 但是,你确实有一个通用的观点,它是一个查询机制的定义...然而,它充满了大量的投影能力。 - Pompair
2
坚持你已经做过的方式,Linq很棒,但下面的例子并没有真正清晰地表达意图。用它来查询,但不是用于所有情况。 - BenjaminPaul
3
我认为他在做什么并不是非常明显?一个简单的for循环更加清晰易懂。 - BenjaminPaul
1
@Pompair 此外,这并不会给你“添加了项目的相同列表”,而是会给你“一个新列表”——如果其他任何东西保留对旧列表的引用,则无法使用。 - Rawling
哦,LINQ是一个转换系统。它以可枚举对象作为输入,并可以对数据执行某些操作。在其他语言中,C#所称的“Select”被称为“map”。这是一个转换函数,它会为每个元素执行并为每个元素创建新的内容。用户想要的是为每个元素创建两个元素。一个简单的x => [ "", x ]。而SelectMany正好可以做到这一点。为什么创建一个List并向其中添加元素更清晰呢?实际上,理解Selman22编写的for循环的真正意图要困难得多。 - David Raab
4个回答

14
你可以使用一个名为 Intersperse 的扩展方法。这样,意义清晰且代码可重用。代码取自Enumerable.Intersperse的扩展方法,稍作修改以在第一位置包括一个空字符串。请保留HTML标签。
public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> source, T element)
{
    foreach (T value in source)
    {
        yield return element;
        yield return value;
    }
}

3
@Pompair 我认为这个解决方案更加优雅 - 当你使用它时,它是一个内联程序(inline),并且更易于阅读:var intersperced = list.Intersperce(""); - dav_i

9
这里有一种方法来实现:

以下是具体步骤:

list = list.SelectMany(x => new [] { string.Empty, x }).ToList();

但需要注意的是,这样会创建不必要的数组。如果您的列表足够大,这可能会成为一个问题。相反,我建议使用循环创建一个新的具有容量的列表并填充它:

var newList = new List<string>(list.Count * 2);
int j = 0;
for(int i = 0; i < list.Count * 2; i++)
    newList.Add(i % 2 == 0 ? string.Empty : list[j++]);

这样可以避免每次添加或插入项目时调整列表的大小。

一个 List<T> 在每次添加/删除元素时都不会被重新调整大小。内部它使用数组作为支持字段,支持字段的大小通过2的幂级数增加(从4开始)。默认情况下,大小永远不会减小。要获取支持字段的大小,可以使用 list.Capacity。如果调用了 list.TrimExcess(),则支持字段才会被缩小。因此,直接设置列表的大小可能仍然更快,但实际上这几乎没有任何影响。 - David Raab

2

您可以使用SelectMany LINQ扩展来完成此操作:

void Main()
{
    List<String> items = new List<String>()
    {
        "1",
        "2",
        "3"
    };

    var result = items
        .SelectMany(item => new String[] {"Some value", item})
        .ToList();

    PrintItems(result);
}

void PrintItems<T>(IEnumerable<T> items)
{
    foreach(var item in items)
    {
        Console.WriteLine(item);
    }
}

但是,正如你所理解的那样,这并不是最有效的方法。

不知道是否有一种方法可以只添加“某个值”一次到结果数组中来实现这个。 - bcr

1

使用Aggregate的另一个一行代码:

List<string> result = list.Aggregate(new List<string>(list.Count * 2), (a, x) => { a.Add(""); a.Add(x); return a; });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接