Say I have a lot of the following documents:
{
_id: “abc”,
values: {
0: { 0: 999999, 1: 999999, …, 59: 1000000 },
1: { 0: 2000000, 1: 2000000, …, 59: 1000000 },
…,
58: { 0: 1600000, 1: 1200000, …, 59: 1100000 },
59: { 0: 1300000, 1: 1400000, …, 59: 1500000 }
}
}
{
_id: “def”,
values: {
0: { 0: 999999, 1: 999999, …, 59: 1000000 },
1: { 0: 2000000, 1: 2000000, …, 59: 1000000 },
…,
58: { 0: 1600000, 1: 1200000, …, 59: 1100000 },
59: { 0: 1300000, 1: 1400000, …, 59: 1500000 }
}
}
which is basically a multidimensional array of 60x60 items.
can aggregation (or any other mongodb construct) be used to easily sum the two (or more) matrixes? i.e. values[x][y] of both abc and def are summed together, and the same is done for all other elements?
Ideally the output would be a similar multidimensional array.
This answer seems to suggest this is possible with 1 dimensional array but I am not sure for multidimensional.
EDIT:
This is an example with real data in a format which is slightly different:
db.col.find({}, { _id: 0, hit: 1 })
{ "hit" : [ [ 570, 0, 630, 630, 636, 735, 672, 615, 648, 648, 618, 0 ],
[ 492, 0, 471, 471, 570, 564, 0, 590, 513, 432, 471, 477 ],
[ 387, 0, 0, 0, 0, 0, 0, 456, 0, 480, 351, 415 ],
[ 432, 528, 0, 0, 495, 509, 0, 579, 0, 552, 0, 594 ],
[ 558, 603, 594, 624, 672, 0, 0, 705, 783, 0, 756, 816 ],
[ 0, 858, 951, 1027, 0, 0, 1058, 1131, 0, 0, 1260, 1260 ],
[ 1269, 0, 1287, 0, 1326, 0, 1386, 1386, 1470, 0, 0, 0 ],
[ 1623, 0, 1695, 1764, 1671, 1671, 0, 1824, 1872, 0, 0, 0 ],
[ 1950, 1894, 2034, 2034, 0, 0, 1941, 0, 2070, 1911, 2049, 2055 ],
[ 2052, 2052, 0, 0, 0, 2085, 2007, 2073, 0, 0, 0, 1941 ],
[ 1878, 1896, 0, 1875, 0, 0, 1677, 0, 1722, 0, 1545, 0 ],
[ 0, 0, 1317, 1469, 1501, 1634, 1494, 0, 0, 1290, 0, 0 ],
[ 0, 1485, 1375, 1491, 1530, 1407, 0, 0, 0, 1611, 0, 0 ],
[ 1652, 1800, 1686, 1643, 1923, 0, 0, 0, 1737, 1604, 1797, 0 ],
[ 1842, 1806, 0, 1830, 1896, 1947, 0, 1710, 1734, 1725, 0, 0 ],
[ 0, 0, 1932, 0, 1908, 1878, 1941, 1931, 2007, 2013, 1995, 1995 ],
[ 0, 2025, 2004, 1927, 0, 0, 1939, 1835, 1962, 1863, 0, 1815 ],
[ 0, 0, 1839, 1755, 1821, 1821, 1751, 1656, 0, 0, 1467, 0 ],
[ 0, 1632, 1546, 1449, 0, 1551, 1449, 0, 0, 1554, 0, 1491 ],
[ 1463, 1411, 0, 1491, 0, 0, 1551, 1467, 0, 0, 0, 1464 ],
[ 0, 0, 1311, 0, 0, 1471, 0, 0, 1581, 0, 1368, 1368 ],
[ 1296, 0, 0, 0, 1176, 1381, 0, 1170, 1194, 1194, 1193, 1137 ],
[ 0, 1244, 1221, 1039, 0, 1041, 930, 921, 1033, 813, 0, 0 ],
[ 0, 0, 0, 1010, 0, 0, 918, 783, 0, 609, 693, 645 ] ] }
And this is the appropriate query (thanks to Veeram in the comments for fixing my code):
db.col.aggregate([
{ $project: { _id: 0, hit: 1 } },
{ $unwind: { path: "$hit", includeArrayIndex: "x" } },
{ $unwind: { path: "$hit", includeArrayIndex: "y" } },
{ $group: { _id: { x: "$x", y: "$y" }, hit: { $sum: "$hit" } } },
{ $sort: { "_id.x": 1, "_id.y": 1 } },
{ $group: { _id: "$_id.x", hit: { $push: "$hit" } } },
{ $sort: { "_id": 1 } },
{ $group: { _id: null, hit: { $push: "$hit" } } }
])
You need two operators to deal with dynamic properties: $objectToArray and $arrayToObject. To sum the values from all documents you can try to represent each x,y pair as single document (using $unwind) and then use several $group stages to get single document as a result. To get the initial order of your rows and columns you can apply $sort twice:
db.col.aggregate([
{
$project: {
values: {
$map: {
input: { $objectToArray: "$values" },
as: "obj",
in: { k: "$$obj.k", v: { $objectToArray: "$$obj.v" } }
}
}
}
},
{
$unwind: "$values"
},
{
$unwind: "$values.v"
},
{
$project: {
x: "$values.k",
y: "$values.v.k",
value: "$values.v.v"
}
},
{
$group: {
_id: { x: "$x", y: "$y" },
value: { $sum: "$value" }
}
},
{
$sort: {
"_id.y": 1
}
},
{
$group: {
_id: "$_id.x",
v: { $push: { k: "$_id.y", v: "$value" } }
}
},
{
$sort: {
"_id": 1
}
},
{
$group: {
_id: null,
values: { $push: { k: "$_id", v: "$v" } }
}
},
{
$project: {
values: {
$arrayToObject: {
$map: {
input: "$values",
as: "obj",
in: {
k: "$$obj.k",
v: { $arrayToObject: "$$obj.v" }
}
}
}
}
}
}
])
For your sample data it outputs:
{
"_id" : null,
"values" : {
"0" : {
"0" : 1999998,
"1" : 1999998,
"59" : 2000000
},
"1" : {
"0" : 4000000,
"1" : 4000000,
"59" : 2000000
},
"58" : {
"0" : 3200000,
"1" : 2400000,
"59" : 2200000
},
"59" : {
"0" : 2600000,
"1" : 2800000,
"59" : 3000000
}
}
}
Related
I have this document:
[
{
"username_id": "user01",
"passwordList": [
{
"tstamp": 101,
"tempInd": 0,
"pass": "aaaa"
},
{
"tstamp": 102,
"tempInd": 0,
"pass": "bbbbb"
},
{
"tstamp": 103,
"tempInd": 0,
"pass": "ccccc"
},
{
"tstamp": 100,
"tempInd": 1,
"pass": "99999"
}
]
}
]
What I want is to remove from the passwordList the element which has the lowest tstamp with tempInd equal to 0. This is my expected output:
[
{
"username_id": "user01",
"passwordList": [
{
"tstamp": 102,
"tempInd": 0,
"pass": "bbbbb"
},
{
"tstamp": 103,
"tempInd": 0,
"pass": "ccccc"
},
{
"tstamp": 100,
"tempInd": 1,
"pass": "99999"
}
]
}
]
This is my attempt:
db.collection.update([
{"username_id": "user01" } ,
{"$pull":{"passwordList": { "$elemMatch": { "tempInd": 0 , "tstamp": {"$min": "$passwordList.tstamp"} } } } }
])
Any suggestion?
Thanks!
You can do it like this:
db.collection.update(
{ "username_id": "user01" } ,
[
{
$set: {
passwordList: {
$filter: {
input: '$passwordList',
as: 'filter1Password',
cond: {
$ne: [
'$$filter1Password',
{
$first: {
$sortArray: {
input: {
$filter: {
input: '$passwordList',
as: 'filter2Password',
cond: {
$eq: ['$$filter2Password.tempInd', 0]
}
}
},
sortBy: {
tstamp: 1
}
}
}
}
]
}
}
}
}
}
]
)
Working from the inside out:
The innermost $filter operator discards all array elements whose tempInd is not 0.
The $sortArray operator sorts the result of step 1 by tstamp, ascending. (note that $sortArray is only available in Mongo 5.2 and newer)
The $first operator returns the first element of the array returned by step 2 (this would be the element with the lowest tstamp whose tempInd is 0)
The $filter operator returns all elements of the passwordList array that are NOT equal to the result of step 3. (note that if the array has multiple elements that all match the result of step 3, all of them will be removed)
The $set operator sets passwordList to be the result of step 4.
I have two versions for the same logic. Below are the two codes:
/*Code 1*/
this.state.dataModel[0].route.routeLine = this.props.routeLine.join(' ');
/*Code 2*/
this.setState(prevState => {
const newDataModel = [...prevState.dataModel];
newDataModel[0].route.routeLine = this.props.routeLine.join(' ');
return {dataModel: newDataModel};
});
/* Common Code */
let finalData = { ...this.state.dataModel[0],
hasLocationChanged: this.state.isLocationEditedForConsSeg };
let data = JSON.stringify(finalData)
console.log('Stringify Data ',JSON.stringify(data))
When I use Code 1 the object is converted to JSON strings without data loss. But if I use Code 2 I am loosing data in JSON strings when using JSON.stringify().But according to my understanding of react Code 1 is not the correct way to mutate state
Below is the finalData in JSON object:
{
"layer": 1,
"layerName": "ConstructionSegment",
"layerId": 7384,
"agencyId": 79,
"lpModel": {
"lastProjectType": 2,
"lastProjectSurf": 2,
"gravelType": 0,
"gravelTreatment": 0,
"bitType": 1,
"concrType": 0,
"astType": 0,
"compType": 0,
"lastProjectYear": 2002,
"lpDepth": 7,
"totalDepth": 7,
"lastProjectMile": "0",
"comment": "",
"lastProjectYearEst": 0,
"lpDepthEst": 0,
"totalDepthEst": 0
},
"bModel": {
"topBaseType": 3,
"topBaseDpt": 23,
"topBaseYear": 1973,
"topBaseTrt": 6,
"bottomBaseType": 2,
"bottomBaseDpt": 23,
"bottomBaseYear": 0,
"bottomBaseTrt": 1,
"fabric": 0,
"subgradeStrength": 100,
"subgradeStrengthType": 5,
"lastSGImpYear": 0,
"subgradeTrt": 1,
"topBaseTypeEst": 0,
"topBaseDptEst": 0,
"topBaseYearEst": 0,
"bottomBaseTypeEst": 0,
"bottomBaseDptEst": 0,
"bottomBaseYearEst": 0,
"subgradeStrengthEst": 0,
"lastSGImpYearEst": 0
},
"xModel": {
"laneWidth": 4,
"numLanes": 2,
"rtShoulderTotal": 5,
"rtShoulderPaved": 0,
"gradingYear": 1973,
"curbs": 0,
"inslopeRatio": 0,
"paveSloughWidth": 0,
"sloughRatio": 0,
"edgelineTrt": 0,
"centerlineTrt": 0,
"medianType": 0,
"medianWidth": 0,
"rightOfWay": 100,
"sectionOwner": 2,
"gradingYearEst": 0
},
"maintenance": {
"blade": [],
"regravel": [],
"reshape": [],
"spotRep": [],
"dustControl": [],
"surfacing": [],
"crackSeal": [],
"patching": [],
"striping": [],
"cpr": [],
"crackSealConcrete": [],
"reApplyAst": []
},
"pavementcondition": {
"PlannedProjType": 0
},
"route": {
"id": 12804,
"start": "47.04905, -95.722035",
"end": "47.107359, -95.721154",
"routeLength": "4.03",
"routeArray": [
"47.04906,-95.72206",
"47.06001,-95.72184",
"47.06281,-95.72179",
"47.07266,-95.7216",
"47.07647,-95.72154",
"47.07991,-95.72149",
"47.08224,-95.72154",
"47.08528,-95.72169",
"47.08749,-95.72178",
"47.08846,-95.72179",
"47.09029,-95.72181",
"47.09236,-95.72178",
"47.09425,-95.72172",
"47.09729,-95.72154",
"47.10185,-95.721",
"47.10291,-95.72095",
"47.10738,-95.72119"
],
"routePointArray": [
[
47.04906,
-95.72206
],
[
47.06001,
-95.72184
],
[
47.06281,
-95.72179
],
[
47.07266,
-95.7216
],
[
47.07647,
-95.72154
],
[
47.07991,
-95.72149
],
[
47.08224,
-95.72154
],
[
47.08528,
-95.72169
],
[
47.08749,
-95.72178
],
[
47.08846,
-95.72179
],
[
47.09029,
-95.72181
],
[
47.09236,
-95.72178
],
[
47.09425,
-95.72172
],
[
47.09729,
-95.72154
],
[
47.10185,
-95.721
],
[
47.10291,
-95.72095
],
[
47.10738,
-95.72119
]
],
"startPoint": [
[
47.04905,
-95.72204
]
],
"endPoint": [
[
47.10736,
-95.72115
]
],
"waypoints": [],
"isPolyLine": false,
"hwyNum": "34",
"segDesc": "",
"routeLine": "47.04906,-95.72206 47.06001,-95.72184 47.06281,
-95.72179 47.07266,-95.7216 47.07647,
-95.72154 47.07991,-95.72149 47.08224,-95.72154 47.08528,
-95.72169 47.08749,-95.72178 47.08846,-95.72179 47.09029,
-95.72181 47.09236,-95.72178 47.09425,
-95.72172 47.09729,-95.72154 47.10185,-95.721 47.10291,
-95.72095 47.10738,-95.72119"
}
}
I can see the data routeLine in the console.log() statement if I use Code 1 but I cannot see the data routeLine in the console.log() if I use Code 2
You are losing it becasue setState is asynchronous. If you call this.setState and soon after you read this.state you'll notice that it's not immediately updated.
See this question for further details please and setState docs.
edit1: I created a sandbox example for you. Check code in App.js, you'll se 4 click functions and a logState function. click1 and click2 do what you did in your original post, click3 and click4 propose a solution to overcome the problem.
Your routeLine is a string: "47.04906,-95.72206 47.06001,-95.72184 47.06281", not an array as you might think. You need to first convert it to an array, and then apply a join():
this.state.dataModel[0].route.routeLine.split(' ').join(). You might need to revise your state and make sure you are parsing data according to your expectations.
Greetings fellow fans of MongoDB!
I've got here a data structure with board game data where achieved scores (after every round) are tracked as nested arrays associated with the player's name. Note that with each board game there's a different set of players:
{
"BoardGames" : {
"Game1" : {
"players" : {
"Anne" : [97, 165, 101, 67],
"Pete" : [86, 115, 134, 149],
"Scott" : [66, 89, 103, 74],
"Jane" : [113, 144, 125, 99],
"Katie" : [127, 108, 98, 151]
}
},
"Game2" : {
"players" : {
"Margot" : [1, 0, 0, 0],
"Pete" : [0, 0, 1, 1],
"Michael" : [0, 0, 0, 0],
"Jane" : [0, 0, 1, 0]
}
},
"Game3" : {
"players" : {
"Chris" : [6, 2, 4, 0, 5, 7],
"Pete" : [4, 5, 2, 5, 3, 1, 4],
"Julia" : [3, 7, 4, 0],
"Tom" : [3, 2, 4, 8, 2, 6, 7]
}
},
}
Game1: Players earn as many victory points per round as they can
Game2: Winning around earns 1, losing a round 0
Game3: Players may leave after every round, hence some players have played more rounds than others, so these arrays are different in their length
So, here are my questions:
Which player got the most points in each game? Who the least?
Who is the winner in the first round? 2nd round, etc.
Who is sitting on 1st, 2nd and 3rd rank from all played games?
I've done quite some queries with mongo, but so far with a nested array that is attached to a flexible/unpredictable parent node I have no idea how to write a query. Also, maybe this is not the best way how I structured the data. So in case you have a better idea, I'd be happy to learn!
Cheers!
P.S.: The insertMany statement to above JSON data:
db.boardGames.insertMany([
{
"_id" : 1,
"Game1" : {
"players" : {
"Anne" : [97, 165, 101, 67],
"Pete" : [86, 115, 134, 149],
"Scott" : [66, 89, 103, 74],
"Jane" : [113, 144, 125, 99],
"Katie" : [127, 108, 98, 151]
}
},
"Game2" : {
"players" : {
"Margot" : [1, 0, 0, 0],
"Pete" : [0, 0, 1, 1],
"Michael" : [0, 0, 0, 0],
"Jane" : [0, 0, 1, 0]
}
},
"Game3" : {
"players" : {
"Chris" : [6, 2, 4, 0, 5, 7],
"Pete" : [4, 5, 2, 5, 3, 1, 4],
"Julia" : [3, 7, 4, 0],
"Tom" : [3, 2, 4, 8, 2, 6, 7]
}
}
}]);
the schema you have is not ideal. if it was something like this: https://mongoplayground.net/p/o8m205t9UKG then you can query like the following:
find winner of each game:
db.collection.aggregate(
[
{
$set: {
Players: {
$map: {
input: "$Players",
as: "x",
in: {
Name: "$$x.Name",
TotalScore: { $sum: "$$x.Scores" }
}
}
}
}
},
{
$unwind: "$Players"
},
{
$sort: { "Players.TotalScore": -1 }
},
{
$group: {
_id: "$Name",
Winner: { $first: "$Players.Name" }
}
}
])
find top 3 ranking players across all games:
db.collection.aggregate(
[
{
$set: {
Players: {
$map: {
input: "$Players",
as: "x",
in: {
Name: "$$x.Name",
TotalScore: { $sum: "$$x.Scores" }
}
}
}
}
},
{
$unwind: "$Players"
},
{
$group: {
_id: "$Players.Name",
TotalScore: { $sum: "$Players.TotalScore" }
}
},
{
$sort: { TotalScore: -1 }
},
{
$limit: 3
},
{
$group: {
_id: null,
TopRanks: { $push: "$_id" }
}
},
{
$project: {
_id: 0,
TopRanks: 1
}
}
])
find the winner of each round across all games
db.collection.aggregate(
[
{
$set: {
"Players": {
$map: {
input: "$Players",
as: "p",
in: {
Scores: {
$map: {
input: "$$p.Scores",
as: "s",
in: {
Player: "$$p.Name",
Round: { $add: [{ $indexOfArray: ["$$p.Scores", "$$s"] }, 1] },
Score: "$$s"
}
}
}
}
}
}
}
},
{
$unwind: "$Players"
},
{
$unwind: "$Players.Scores"
},
{
$replaceRoot: { newRoot: "$Players.Scores" }
},
{
$sort: {
Round: 1,
Score: -1
}
},
{
$group: {
_id: "$Round",
Winner: { $first: "$Player" }
}
},
{
$project: {
_id: 0,
Round: "$_id",
Winner: 1
}
},
{
$sort: {
Round: 1
}
}
])
Lets say I have a mongo database that look like this:
[
{
dec: 5972,
bin: [0,0,0,1,0,1,1,1,0,1,0,1,0,1,0,0]
},
{
dec: 397250,
bin: [1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,0]
},
{
dec: 5,
bin: [0,1,0,1]
},
{
dec: 123,
bin: [0,1,1,1,1,0,1,1]
}
]
How do I query the database to give me all the objects that the number of '0' and '1' in their bin property is equal.
For example, I would expect the result to be only:
{
dec: 5,
bin: [0,1,0,1]
}
Here you go:
db.test.aggregate([
{
$project: {
dec: "$dec",
bin: "$bin",
original_bin: "$bin"
}
},
{
$unwind: "$bin"
},
{
$group: {
_id: {
dec: "$dec",
bin: "$bin",
original_bin: "$original_bin"
},
total: {
$sum: 1
}
}
},
{
$group: {
_id: {dec: "$_id.dec", original_bin: "$_id.original_bin"},
sums: {
$addToSet: "$total"
}
}
},
{
$match: {
sums: {
$size: 1
}
}
},
{
$project: {
_id: 0,
dec: "$_id.dec",
bin: "$_id.original_bin"
}
}
]);
I have a sample data like this:
{"dec":1,bin: [0,0,0,1,0,1,1,1,0,1,0,1,0,1,0,0]}
{"dec":2,bin: [0,1,0,1,0,0]}
{"dec":3,bin: [1,0,1,0,1,0]}
{"dec":4,bin: [0,1,0,0]}
{"dec":5,bin: [0,0,0,1,1,1]}
{"dec":6,bin: [0,1]}
Given query on the sample data returns the desired arrays:
{ "dec" : 6, "bin" : [ 0, 1 ] }
{ "dec" : 3, "bin" : [ 1, 0, 1, 0, 1, 0 ] }
{ "dec" : 5, "bin" : [ 0, 0, 0, 1, 1, 1 ] }
I have coded myself into a corner and can't figure out how to calculate the subtotals (per crop) and totals (all crops). I have the expected values hard-coded right now but need to figure out how to calculate them.
I have a plunker.
I am using budget.json to simulate the call to the database in the factory (defined in budget.js). The BudgetsController is also defined in budget.js.
The hard-coded totals begin at line 35 of budgets.js. I tried several of the LoDash methods to calculate the totals but can't seem to find the pattern that I can replicate for each crop and I know the Totals total would follow the same pattern but just using the subtotals.
Any help is appreciated!
CODE of budget.js:
(function(){
'use strict';
angular
.module('ARM')
.factory('ExpensesFactory', function ExpensesFactory(
$http, $q
) {
return {
getBudget: getBudget
};
function getBudget(id){
return $http.get('budget.json');
}
})
.controller('BudgetsController', BudgetsController);
BudgetsController.$inject = ['$scope', 'ExpensesFactory'];
function BudgetsController(
$scope, ExpensesFactory
){
ExpensesFactory.getBudget('1')
.then(function success(rsp){
var arr = rsp.data;
var flattened = _.flatten(arr);
var grped = _.groupBy(flattened, function(item) {
return item.crop;
});
$scope.uses = grped;
//TODO: Crop and Loan Budget Totals
$scope.uses.totals = [
//CORN
[
{
"arm": 178,
"dist": 197.91,
"other": 115,
"peracre": 490.91,
"calc_arm": 61837.2,
"calc_dist": 68753.934,
"calc_other": 39951,
"calc_total": 170542.134
}
],
//SOYBEANS
[
{
"arm": 145,
"dist": 69.73,
"other": 74.35,
"peracre": 289.08,
"calc_arm": 84143.5,
"calc_dist": 40464.319,
"calc_other": 43145.305,
"calc_total": 167753.124
}
],
//SORGHUM
[
{
"arm": 0,
"dist": 0,
"other": 0,
"peracre": 0,
"calc_arm": 0,
"calc_dist": 0,
"calc_other": 0,
"calc_total": 0
}
],
//WHEAT
[
{
"arm": 0,
"dist": 0,
"other": 0,
"peracre": 0,
"calc_arm": 0,
"calc_dist": 0,
"calc_other": 0,
"calc_total": 0
}
],
//COTTON
[
{
"arm": 0,
"dist": 0,
"other": 0,
"peracre": 0,
"calc_arm": 0,
"calc_dist": 0,
"calc_other": 0,
"calc_total": 0
}
],
//RICE
[
{
"arm": 0,
"dist": 0,
"other": 0,
"peracre": 0,
"calc_arm": 0,
"calc_dist": 0,
"calc_other": 0,
"calc_total": 0
}
],
//PEANUTS
[
{
"arm": 0,
"dist": 0,
"other": 0,
"peracre": 0,
"calc_arm": 0,
"calc_dist": 0,
"calc_other": 0,
"calc_total": 0
}
],
//SUGAR CANE
[
{
"arm": 0,
"dist": 0,
"other": 0,
"peracre": 0,
"calc_arm": 0,
"calc_dist": 0,
"calc_other": 0,
"calc_total": 0
}
],
//TOTALS
[
{
"arm": 0,
"dist": 0,
"other": 0,
"peracre": 0,
"calc_arm": 155999,
"calc_dist": 36530,
"calc_other": 87223,
"calc_total": 279752
}
]
];
var uniqExp = _.uniq(_.pluck(flattened, 'expense'));
$scope.exp = uniqExp;
});
} // end BudgetsController fn
})();
Let's see what you've got:
_.groupBy: returns an object whose keys are crop's names;
_.map: iterates over grped keys (crop names) and returns an array;
item.reduce(): native array method, accumulates some values through the whole array
current: reduces passes a different element from array, each time;
previous: this one we're are in control, the first time it contains the value of the second reduce() parameter, this is why I've passed an fixed object. This object is modified each iteration.
More information about Array.prototype.reduce: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Sample:
$scope.uses = _.map(grped, function (item, key) {
return item.reduce(function (previous, current) {
// in the nth iteration, each previous property will be equal to sum(arr[0].property...arr[n-1].property)
previous.arm += current.arm;
previous.dist += current.dist;
previous.other += current.other;
// other fields should be summed here
// remember to return the accumulator
return previous;
},
/* initialize each property to zero, otherwize it won't work */
{crop: key, arm: 0, dist: 0, other: 0});
});