2008-11-22 2 views
11

У меня есть список < int []> myList, где я знаю, что все массивы int [] имеют одинаковую длину - ради аргумента, скажем, у меня есть 500 массивов, каждый из которых содержит 2048 элементов. Я хотел бы суммировать все 500 из этих массивов, чтобы дать мне один массив, длиной 2048 элементов, где каждый элемент является суммой всех одинаковых позиций во всех других массивах.Как суммировать список <> массивов

Очевидно, что это тривиально в императивном коде:

int[] sums = new int[myList[0].Length]; 
foreach(int[] array in myList) 
{ 
    for(int i = 0; i < sums.Length; i++) 
    { 
     sums[i] += array[i]; 
    } 
} 

Но мне было интересно, если там был хороший Linq или Enumerable.xxx техника?

ответ

10

Редактировать: Ouch ... Это стало немного сложнее, пока я не смотрел. Изменение требований может быть реальной PITA.

Хорошо, так возьмите каждую позицию в массиве, и просуммировать:

var sums = Enumerable.Range(0, myList[0].Length) 
      .Select(i => myList.Select(
        nums => nums[i] 
       ).Sum() 
      ); 

Это вроде некрасиво ... но я думаю, что версия заявление будет еще хуже.

4

EDIT: Я оставил это здесь ради интереса, но принятый ответ намного приятнее.

EDIT: Хорошо, моя предыдущая попытка (см история изменений) был в основном совершенно неправильно ...

Вы можете сделать это с помощью одной строки LINQ, но это ужасно:

var results = myList.SelectMany(array => array.Select(
               (value, index) => new { value, index }) 
        .Aggregate(new int[myList[0].Length], 
           (result, item) => { result[item.index] += value; return result; }); 

Я не тестировал его, но думаю, он должен работать. Я бы не рекомендовал его. SelectMany сглаживает все данные в последовательности пар - каждая пара является значением и его индексом в своем исходном массиве.

Шаг агрегации полностью не чист - он изменяет свой аккумулятор, когда он идет, добавляя правильное значение в нужную точку.

Если кто-нибудь не может придумать способ принципиального поворота исходных данных (в этот момент мой более ранний ответ - это то, что вы хотите), я подозреваю, что вам лучше всего сделать это не-LINQ.

+0

Спасибо Jon - не видел оригинал, но я думаю, что это, возможно, был прав. Я не хочу суммировать каждый массив, я хочу суммировать элементы каждого массива с другими массивами. Я улучшу вопрос. – 2008-11-22 20:18:02

+0

OK, код * был * неправильный и был неполным. – 2008-11-22 20:21:38

1

ОК, предполагая, что мы можем предположить, что сумма чисел в каждой позиции над списком массивов сама по себе будет вписываться в int (что является изворотливым допущением, но я сделаю это в любом случае, чтобы облегчить работу) :

int[] sums = 
    Enumerable.Range(0, listOfArrays[0].Length-1). 
     Select(sumTotal => 
      Enumerable.Range(0, listOfArrays.Count-1). 
       Aggregate((total, listIndex) => 
        total += listOfArrays[listIndex][sumTotal])).ToArray(); 

EDIT - D'oh. По какой-то причине. Сначала меня уклонились. Это немного лучше. Это небольшой взлом, потому что sumTotal действует как вход (позиция в массиве, который используется в вызове Aggregate), и выходная сумма в результирующем IEnumerable, который является интуитивно понятным.

Откровенно это гораздо страшнее, чем делать это старый-fasioned путь :-)

0

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

var result = xs.Aggregate(
    (a, b) => Enumerable.Range(0, a.Length).Select(i => a[i] + b[i]).ToArray() 
); 
2

Это работает с любым 2-х последовательностей, а не только массивами:

var myList = new List<int[]> 
{ 
    new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
    new int[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 } 
}; 

var sums = 
    from array in myList 
    from valueIndex in array.Select((value, index) => new { Value = value, Index = index }) 
    group valueIndex by valueIndex.Index into indexGroups 
    select indexGroups.Select(indexGroup => indexGroup.Value).Sum() 

foreach(var sum in sums) 
{ 
    Console.WriteLine(sum); 
} 

// Prints: 
// 
// 11 
// 22 
// 33 
// 44 
// 55 
// 66 
// 77 
// 88 
// 99 
1

Вот один, который торгует операторную простоту Linq с производительностью.

var colSums = 
    from col in array.Pivot() 
    select col.Sum(); 

public static class LinqExtensions { 
    public static IEnumerable<IEnumerable<T>> Pivot<T>(this IList<T[]> array) { 
     for(int c = 0; c < array[ 0 ].Length; c++) 
      yield return PivotColumn(array, c); 
    } 
    private static IEnumerable<T> PivotColumn<T>(IList<T[]> array, int c) { 
     for(int r = 0; r < array.Count; r++) 
      yield return array[ r ][ c ]; 
    } 
}