如何汇总列表<>数组

时间:2008-11-22 20:03:54

标签: c# linq

我有一个List< 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技术?

7 个答案:

答案 0 :(得分:11)

编辑:哎哟...当我不看时,这变得有点困难。不断变化的要求可能是真正的PITA。

好的,所以取数组中的每个位置,然后求和:

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

这有点难看......但我认为声明版本会更糟糕。

答案 1 :(得分:4)

编辑:为了感兴趣,我把它留在这里,但接受的答案要好得多。

编辑:好的,我以前的尝试(参见编辑历史)基本上是完全错误的......

可以用一行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将所有数据展平为一系列对 - 每对都是值,其索引在其原始数组中。

Aggregate步骤完全是非纯粹的 - 它通过在正确的位置添加正确的值来修改其累加器。

除非有人能想出一种基本上转动原始数据的方法(此时我的答案就是你想要的),我怀疑你最好不采用非LINQ方式。

答案 2 :(得分: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

答案 3 :(得分: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();
编辑 - D'哦。出于某种原因。选择最初避开了我。那有点好。这是一个轻微的黑客攻击,因为sumTotal既作为输入(在聚合调用中使用的数组中的位置)又作为生成的IEnumerable中的输出总和,这是违反直觉的。

坦率地说,这比以古老的方式做得更加可怕: - )

答案 4 :(得分: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 ];
    }
}

答案 5 :(得分:0)

我会这样做......但是这个解决方案可能实际上非常慢,所以你可能想在将其部署到性能关键部分之前运行基准测试。

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

答案 6 :(得分:0)

可以使用Zip和Aggregate完成。这个问题太老了,以至于Zip当时不在。无论如何,这是我的版本,希望它能对某人有所帮助。

List<int[]> myListOfIntArrays = PopulateListOfArraysOf100Ints();
int[] totals = new int[100];
int[] allArraysSum = myListOfIntArrays.Aggregate(
    totals,
    (arrCumul, arrItem) => arrCumul.Zip(arrItem, (a, b) => a + b))
    .ToArray();