tags:

views:

195

answers:

3

The context


public class Item
{
    public int Index;
    public string Text;
}

...

var items = new List<Item>
{
    new Item {Index=1, Text="Data #1"},
    new Item {Index=8, Text="Data #8"},
    new Item {Index=4, Text="Data #4"},
};

The code


var data = (from item in items
           orderby item.Index
           select item).ToList();

The result


Data[0] = Data #1
Data[1] = Data #4
Data[2] = Data #8

What is wanted


Data[0] = null
Data[1] = Data #1
Data[2] = null
Data[3] = null
Data[4] = Data #4
Data[5] = null
Data[6] = null
Data[7] = null
Data[8] = Data #8

How to achieve this with Linq?

+5  A: 

This does what you want:

var result = Enumerable.Range(0, items.Max(x => x.Index) + 1)
                       .Select(i => items.Where(x => x.Index == i)
                                         .Select(x => x.Text)
                                         .SingleOrDefault())
                       .ToList();

Result:

result[0] = null
result[1] = Data #1
result[2] = null
result[3] = null
result[4] = Data #3
result[5] = null
result[6] = null
result[7] = null
result[8] = Data #8

You could improve performance by storing the original data in a Dictionary:

Dictionary<int, string> d = items.ToDictionary(x => x.Index, x => x.Text);
List<string> result = new List<string>();
for (int i = 0; i < d.Keys.Max() + 1; ++i)
{
    string s;
    d.TryGetValue(i, out s);
    result.Add(s);
}
Mark Byers
Interesting! Is it possible to use a LINQ expression instead?
Stecy
Dictionary idea seems like totally the way to go, though I would think this wouldn't tihnk this would improve performance because the dictionary has to follow it's key match algorythm instead of an indexer which just goes strait to the memory space.
Jimmy Hoffa
Performance is not a problem since the source list contains at most 20 items.
Stecy
+4  A: 
string[] myData = new string[items.Max(item => item.Index)];

foreach(Item myItem in items)
{
    myData[myItem.Index] = myItem.Data;
}
Jimmy Hoffa
string[] myData // myData[myItem.Index] = myItem.Text
Caspar Kleijne
@Caspar Kleijne: Ah right, duh. I knew I was doing it that way at first for a reason.. fixed, thanks.
Jimmy Hoffa
I like this, although it turns out that the OP wanted a LINQ solution and this isn't really using LINQ. +1 anyway because it's clean and simple.
Mark Byers
I still think this is the cleanest and the fastes one.
Caspar Kleijne
+3  A: 

Basically, what you need is a left join between a sequence of numbers and your list of items:

var result =
    (from i in Enumerable.Range(0, items.Max(it => it.Index) + 1)
     join item in items on i equals item.Index into g
     from item in g.DefaultIfEmpty()
     select item).ToList();
Thomas Levesque
Excellent! That's what I was looking for.
Stecy