【问题标题】:How do I sum a list<> of arrays如何对数组列表<>求和
【发布时间】:2008-11-22 20:03:53
【问题描述】:

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

【问题讨论】:

    标签: c# linq


    【解决方案1】:

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

    好的,那么对数组中的每个位置求和:

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

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

    【讨论】:

    • 我认为即使不是问的那样,该行应该是: int[] nums = myList.Select(array => ((int[])array).Sum() ).ToArray();因为 Array 不包含 Sum 方法。
    • 只要使用 Linq 命名空间,就可以对数组求和——它是一种扩展方法。
    • 糟糕,这不是 linq,是的,我使用的是 List myList = new List();而不是 List myList = new List();
    • 您可能想在该语句的末尾添加一个 ToArray() 以达到要求,但我认为这样做可以。
    • 我认为它有效,但是这一行没有-1,因为第二个参数是(int count): var sums = Enumerable.Range(0, myList[0].Length)
    【解决方案2】:

    编辑:出于兴趣,我将其留在这里,但接受的答案要好得多。

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

    可以用一行 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 方式。

    【讨论】:

    • 感谢 Jon - 没有看到原版,但我认为它可能是正确的。我不想对每个数组求和,我想将每个数组的元素与其他数组相加。我会改进这个问题。
    【解决方案3】:

    这适用于任何 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
    

    【讨论】:

      【解决方案4】:

      好的,假设我们可以假设数组列表中每个位置的整数之和本身将适合一个整数(这是一个狡猾的假设,但无论如何我都会让它变得更容易):

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

      编辑 - 哦。出于某种原因,.Select 最初避开了我。这样好一点。这是一个小技巧,因为 sumTotal 既作为输入(在 Aggregate 调用中使用的数组中的位置)又作为结果 IEnumerable 中的输出总和,这是违反直觉的。

      坦率地说,这比用老式的方式做这件事要可怕得多:-)

      【讨论】:

        【解决方案5】:

        这是一种用性能换取 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 ];
            }
        }
        

        【讨论】:

          【解决方案6】:

          我会按照以下方式进行……但此解决方案实际上可能非常慢,因此您可能希望在将其部署到性能关键部分之前运行基准测试。

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

          【讨论】:

            【解决方案7】:

            可以使用 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();
            

            【讨论】:

              猜你喜欢
              • 2021-11-29
              • 1970-01-01
              • 2017-01-04
              • 1970-01-01
              • 2021-09-01
              • 2017-03-06
              • 1970-01-01
              • 2021-06-16
              • 2012-11-08
              相关资源
              最近更新 更多