gjvdkamp
gjvdkamp

Reputation: 10516

Element-wise summing of arrays

Consider this set of docs:

[
  {_id:1, values:[1, 2, 3]},
  {_id:2, values:[4, 5, 6]}
]

Is there any elegant way to group/sum the arrays into a new array, where the nth element is the sum of the nth elements of the original documents?

{_id:null, values:[5, 7, 9]}

I can sum up specific elements in an aggregation step:

{$group:{
    _id:null,
    Values0: {$sum:{$arrayElemAt:["$values",0]}},
    Values1: {$sum:{$arrayElemAt:["$values",1]}},
    Values2: {$sum:{$arrayElemAt:["$values",2]}}
}},

But I can't get these back into an array. Also something that would require less typing would be highly appreciated :). If this doesn't work I can loop over the bson docs in C# and construct the answer that way but would prefer something in an aggregation pipeline (or mapreduce maybe? Haven't looked into that yet)

Upvotes: 1

Views: 269

Answers (1)

mickl
mickl

Reputation: 49945

You can use $group to get an array of arrays and then use $reduce to process that array. Using $range you can generate indexes that can be used to get each item of both arrays (result and currently processed item). Value at index zero will be an initialValue for $reduce and values at indexes 1...size will be added to the result in every iteration.

db.collection.aggregate([
    {
        $group: {
            _id: null,
            values : {
                $push: "$values"
            }
        }
    },
    {
        $project: {
            result: {
                $reduce: {
                    input: { $slice: [ "$values", 1, { $size: "$values" } ] },
                    initialValue: { $arrayElemAt: [ "$values", 0 ] },
                    in: {
                        $map: {
                            input: { $range: [ 0, { $size: "$$this" } ] },
                            as: "index",
                            in: {
                                $add: [
                                    { $arrayElemAt: [ "$$this", "$$index" ]  },
                                    { $arrayElemAt: [ "$$value", "$$index" ]  }
                                ]
                            }
                        }
                    }
                }
            }
        }
    }
])

Mongo Playground

Upvotes: 1

Related Questions