Как объединить словарь с несколькими (тремя) ключами, уменьшив один или несколько ключей и объединив несколько записей (если они есть) в одну?

У меня есть словарь со следующим определением.

    Dictionary<int[], int> D = new Dictionary<int[], int>();

где ключ представляет собой массив из 3 элементов. Я привожу это как пример, чтобы упростить мой сценарий. (В моем собственном коде ключ представляет собой сложный объект класса, который имеет список из 3-7 элементов для ключа.)

    int[] key;
    key = new int[] { 1, 1, 1 };
    D.Add(key, 1);
    key = new int[] { 1, 1, 2 };
    D.Add(key, 2);
    key = new int[] { 1, 1, 3 };
    D.Add(key, 3);
    key = new int[] { 1, 2, 4 };
    D.Add(key, 4);
    key = new int[] { 2, 1, 1 };
    D.Add(key, 5);
    key = new int[] { 2, 5, 1 };
    D.Add(key, 6);

Я хочу иметь средство для уменьшения количества ключей, т.е. вместо массива из трех элементов я хочу, чтобы в качестве ключа использовался массив из 2 элементов, и чтобы все избыточные значения были объединены в одно значение, чтобы результирующие пары KeyValue выглядели следующим образом. (уменьшение первого индекса для ключей)

    {1 1, 6} //two instances of matching key of {1 1} resulted the value to have 1+5 =6
    {1 2, 2}
    {1 3, 3}
    {2 4, 4}
    {5 1, 6}

person spyronum    schedule 21.10.2013    source источник
comment
почему 1+5, а не 1+2+3? почему 1 2, 2, а не 1 2, 4? откуда взялись ключи 2 4 и 5 1? это не имеет для меня никакого смысла.   -  person BartoszKP    schedule 21.10.2013
comment
Я вынул первый столбец из списка ключей. это оставляет мне второй столбец и третий столбец KeyCollection. и если я возьму только уникальную пару из этих двух столбцов, я получу только 5 ключей, которые я разместил, и в этих 5 только {1 1} имеет несколько экземпляров в первом и пятом элементах моего исходного словаря со значением 1 и 5 соответственно. Поэтому, когда я хочу, чтобы словарь был объединен, эти два значения должны быть объединены, что дает мне 1 + 5 = 6.   -  person spyronum    schedule 21.10.2013


Ответы (1)


Во-первых, ваш словарь, вероятно, работает не так, как вы ожидаете - для типа int[] нет компаратора по умолчанию, поэтому ключи в вашем словаре не будут уникальными (например, у вас может быть два элемента с ключом 1 1 1). Чтобы заставить это работать, вам нужно предоставить пользовательский файл IEqualityComparer<int[]>. Это также понадобится, чтобы решение вашей основной проблемы заработало:

public class IntArrayEqualityComparer : IEqualityComparer<int[]>
{
    public bool Equals(int[] x, int[] y)
    {
        if (x.Length != y.Length)
        {        
            return false;
        }

        return x.Zip(y, (v1, v2) => v1 == v2).All(b => b);
    }

    public int GetHashCode(int[] x)
    {
        return 0;
    }
}

Таким образом, вы должны создать свой словарь следующим образом:

Dictionary<int[], int> D
    = new Dictionary<int[], int>(new IntArrayEqualityComparer());

Возвращаясь к основному вопросу, вот как можно добиться желаемого результата:

var result = D
    .GroupBy(
        kvp => kvp.Key.Skip(1).ToArray(),
        new IntArrayEqualityComparer())
    .ToDictionary(
        g => g.Key,
        g => g.Sum(x => x.Value));
person BartoszKP    schedule 21.10.2013
comment
Спасибо, это должно дать мне основу для работы. Оцените ответ. Любые предложения о том, как управлять этим же со сценарием, где ключ представляет собой список‹› объектов, и если я хочу удалить какой-либо из столбцов, список кортежа ключа? - person spyronum; 21.10.2013
comment
@spyronum В основном то же самое - прежде всего вы должны определить критерий равенства как для исходного ключа, так и для ключа с удаленным столбцом. Концептуально List<T> — это то же самое, что и T[]. - person BartoszKP; 21.10.2013