开发者

Merging sequences by type With LINQ

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;开发者_StackOverflow中文版
    value2ToValue1.Add(item.Key, value1);
    value1TypeEnumerators[item.Value].MoveNext();
}

Any Idea how to do this in LINQ?


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?


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.


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

var value1TypeEnumerators = value1ByType.ToList();

instead of using an enumerator.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜