tags:

views:

100

answers:

4

I want to use LINQ to convert this

IEnumerable<int>[] value1ByType = new IEnumerable<int>[3];
value1ByType[0]= new [] { 0};
value1ByType[1]= new [] {10,11};
value1ByType[2]= new [] {20};

var value2ToType = new Dictionary<int,int> {
{100,0},
{101,1},
{102,2},
{103,1}};

to this

var value2ToValue1 = new Dictionary<int,int> { 
{100, 0},
{101,10},
{102,20},
{103,11}};

Is there a way to do this with LINQ? Without LINQ I would use multiple IEnumerators, one for each IEnumerable of value1ByType. like this:

// create enumerators
var value1TypeEnumerators = new List<IEnumerator<int>>();
for (int i = 0; i < value1ByType.Length; i++)
{
    value1TypeEnumerators.Add(value1ByType[i].GetEnumerator());
    value1TypeEnumerators[i].MoveNext();
}

// create wanted dictionary
var value2ToValue1 = new Dictionary<int, int>();
foreach (var item in Value2ToType)
{
    int value1=value1TypeEnumerators[item.Value].Current;
    value2ToValue1.Add(item.Key, value1);
    value1TypeEnumerators[item.Value].MoveNext();
}

Any Idea how to do this in LINQ?

A: 

What you can do for sure is replace the first part with the following:

var value1TypeEnumerators = value1ByType.ToList();

instead of using an enumerator.

Michael Ulmann
+1  A: 

Not pure but you can at least do ...

var enumerators = value1ByType.Select(v => v.GetEnumerator()).ToArray();
var value2ToValue1 = value2ToType
                        .ToDictionary(x => x.Key, x => { enumerators[x.Value].MoveNext(); return enumerators[x.Value].Current; });

But there are so many ways this could go wrong it begs the question - why was the data in those data-structures anyway? and can you fix that instead? How did you end up with exactly the right number of references in the 2nd data structure to elements in the first?

Hightechrider
I stored from objects(=value2) one variable (=type) in a database table. After that I loaded the automatic generated ids (=value1) together with the type and build the objectToID dictionary.
Janko R
@Janko - maybe some code snippets would help us understand ... what do your classes, tables look like?
Hightechrider
If I use the GetNext Extension of Fedes answers in your code, then your code is the most readable for me (and also the fastest), so I set your answer as the accapted answer.
Janko R
@Hightechrider - maybe I will post another question with the original problem.
Janko R
+1  A: 

I'm pretty sure that @Hightechrider's solution is most performant than this one, but if you really like the syntax sugar way, you can do it like this:

public IDictionary<int, int> MergeSequences(IEnumerable<int>[] value1ByType, Dictionary<int, int> value2ToType)
{
    int pos = 0;
    var value1ByTypePos = from byType in value1ByType
                          select new { Pos = pos++, Enumerator = byType.GetEnumerator() };
    return (from byType in value1ByTypePos
            join toType in value2ToType
            on byType.Pos equals toType.Value
            select new { toType.Key, Value = byType.Enumerator.GetNext() })
           .ToDictionary(pair => pair.Key, pair => pair.Value);
}

I've added an extension method to the IEnumerator interface like this:

public static T GetNext<T>(this IEnumerator<T> enumerator)
{
    if (!enumerator.MoveNext())
        throw new InvalidOperationException();
    return enumerator.Current;
}

Now you have to be aware that any of this solutions can give you slightly different results, depending on how elements in the dictionary are enumerated. For example, another valid result to this code is:

var value2ToValue1 = new Dictionary<int,int> { 
    {100, 0},
    {103, 10},
    {102, 20},
    {101, 11}};

Notice that now 101 is paired with 11 and 103 is paired with 10. If this is a problem, then you should use a SortedDictionary<int, int> when defining value2ToType variable.

Fede
A: 

If I do not care about performance I could also write:

var value2Ordered = Value2ToType.OrderBy(x => x.Value).Select(x=>x.Key);
var value1Ordered = from item in value1ByType from subitem in item select subitem;
var value2ToValue1 = value2Ordered.Zip(value1Ordered, (x, y) => new { Key = x, Value = y })
    .ToDictionary(item => item.Key, item => item.Value);

I used the zip method from a stackoverflow community wiki. I didn't test this with the c#4.0 zip method

Janko R