Слияние последовательностей по типу с LINQ - PullRequest
2 голосов
/ 14 марта 2010

Я хочу использовать LINQ для преобразования

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

к этому

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

Есть ли способ сделать это с помощью LINQ? Без LINQ я бы использовал несколько IEnumerator, по одному для каждого IEnumerable value1ByType как это:

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

Есть идеи, как это сделать в LINQ?

Ответы [ 4 ]

1 голос
/ 14 марта 2010

Не чисто, но вы можете, по крайней мере, сделать ...

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

Но есть так много способов, которыми это может пойти не так, и напрашивается вопрос - почему все-таки данные были в этих структурах данных? и вы можете это исправить вместо этого? Как вы в итоге получили точное количество ссылок во второй структуре данных на элементы первой?

1 голос
/ 15 марта 2010

Я почти уверен, что решение @ Hightechrider наиболее эффективно, чем это, но если вам действительно нравится синтаксический подход, вы можете сделать это так:

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

Я добавил метод расширения в интерфейс IEnumerator следующим образом:

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

Теперь вы должны знать, что любое из этих решений может дать вам немного разные результаты, в зависимости от того, как перечисляются элементы в словаре. Например, другой допустимый результат для этого кода:

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

Обратите внимание, что теперь 101 в паре с 11 и 103 в паре с 10. Если это проблема, тогда вам следует использовать SortedDictionary<int, int> при определении переменной value2ToType.

0 голосов
/ 16 марта 2010

Если меня не волнует производительность, я мог бы написать:

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

Я использовал метод zip из вики сообщества stackoverflow . Я не проверял это методом c # 4.0 zip

0 голосов
/ 14 марта 2010

Что вы можете сделать наверняка, так это заменить первую часть следующим:

var value1TypeEnumerators = value1ByType.ToList();

вместо использования перечислителя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...