8

so I have a list:

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

and I want the list to be like this:

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

A simple back-to-front loop gives me just that:

for (int i = list.Count-1; i >= 0; i--)
   list.Insert(i, string.Empty);

But I'm wondering if there is a more elegant way to do this with LINQ?

İsmet Alkan
  • 5,361
  • 3
  • 41
  • 64
Pompair
  • 7,083
  • 11
  • 60
  • 69
  • 3
    IMO: Linq is good for *quering* - not as much for updating/mutating :) – Jens Kloster Nov 05 '14 at 09:15
  • Please check Selman22's answer, how is that not good? :) But yeah, you do have a generic point, it is a query mechanism per definition...however packed with tons to projection power. – Pompair Nov 05 '14 at 09:22
  • 2
    Stick with the way you have done it, Linq is great but the examples below do not really make the intent clear. Use it for querying but it's not for EVERYTHING. – BenjaminPaul Nov 05 '14 at 09:22
  • 3
    @Pompair IMO it's not very obvious what he is doing? a simple for loop is much clearer. – BenjaminPaul Nov 05 '14 at 09:26
  • 1
    @Pompair Also, this doesn't give you *the same list with added items*, it gives you *a new list* - no good if anything else holds a reference to the old list. – Rawling Nov 05 '14 at 09:27
  • Huh, LINQ is a transformation system. It gets an Enumerable as an input and can do something with the data. What C# called `Select` is in other languages a `map`. A transformation function that gets executed for every element and creates something new for every element. What the user here wants is for every element create two elements. a simple `x => [ "", x ]`. And a `SelectMany` just does that. Why should creating a `List` and adding elements to it more clearer? Actually it is much harder to understand the real intention of the `for` loop written by Selman22. – David Raab Nov 05 '14 at 13:00

4 Answers4

14

You could use an Intersperse extension method. That way, the meaning is clear and the code is reusable. Code taken from Extension method for Enumerable.Intersperse with slight modification to also include an empty string in the first position.

public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> source, T element)
{
    foreach (T value in source)
    {
        yield return element;
        yield return value;
    }
}
Community
  • 1
  • 1
mihai
  • 4,592
  • 3
  • 29
  • 42
  • 3
    @Pompair I'd say this solution is much more elegant - and _is_ an in-liner when you're using it (and easier to read): `var intersperced = list.Intersperce("");` – dav_i Nov 05 '14 at 09:46
9

Here is a way to do it:

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

But it's worth noting that this creates unnecessary arrays.If your list is big enough that might be a problem. Instead I would create a new list with a capacity and populate it using loop:

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++]);

This will avoid resizing the list each time you add or insert items.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
  • A `List` don't get resized every time you add/remove something. Internally it uses an array as a backing field and the size of the baking field is increased by powers of two (starting with 4). And by default the size never gets reduced. To get the size of the backing field you can ask `list.Capacity`. The backing field only gets reduced if you call `list.TrimExcess()`. So directly setting the size of a list can still be faster, but it will probably never matter at all. – David Raab Nov 05 '14 at 12:18
2

You can do it using SelectMany LINQ extension:

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);
    }
}

But as you understand it is not the most effective way.

Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • Wonder if there's a way to do this by adding "Some value" just once to the resulting array. – bcr Jul 25 '18 at 00:13
1

Another one-liner using Aggregate:

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