Reputation: 2991
I'm trying to write a function that iterates through an array of objects, and pushes in new ones (ones that have a unique name), while updating ones that have already been seen.
Say for example, I have this array:
$scope.myArray = [
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Banana", "total": 14, "applicable": 21 },
];
By the end of the function, the new array should be:
$scope.myNewArray = [
{ "name": "Apple", "total": 32, "applicable": 42},
{ "name": "Cherry", "total": 24, "applicable": 54},
{ "name": "Plum", "total": 28, "applicable": 42},
{ "name": "Banana", "total": 14, "applicable": 21 },
];
Here's what I have so far:
$scope.myArray = [
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Banana", "total": 14, "applicable": 21 },
];
$scope.myNewArray = [];
$scope.myArray.filter( function () {
var i = $scope.myNewArray.indexOf($scope.myArray.name);
if ($scope.myNewArray.indexOf($scope.myArray.name) < 0)
$scope.myNewArray.push($scope.myArray);
else {
$scope.myNewArray[i].total += $scope.myArray.total;
$scope.myNewArray[i].applicable += $scope.myArray.applicable;
}
});
The problem I'm encountering is everything gets pushed into the new array. That and I believe my else statement where I'm adding the values to the previous record might be wrong.
Also, hard-coding an array for each name doesn't work as this is just a simple example with a small dataset.
Can anyone lend a hand?
Upvotes: 0
Views: 17242
Reputation: 15442
try this approach:
Array.prototype.reduce
)Object.keys
and Array.prototype.map
)var res = {};
res = Object.keys([
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Banana", "total": 14, "applicable": 21 },
].reduce(function (res, item) {
if (res[item.name]) {
res[item.name].total += item.total;
res[item.name].applicable += item.applicable;
}
else {
res[item.name] = item;
}
return res;
}, res)).map(function(key) {
return res[key];
});
console.log(res);
adding less hardcoded solution:
var myArray = [
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Banana", "total": 14, "applicable": 21 },
];
var res = {};
// add keys for loopable integers which will be summed
var loopables = Object.keys(myArray[0]).filter(function (key) {
return Number.isInteger(myArray[0][key]);
});
res = Object.keys(myArray.reduce(function (res, item) {
if (res[item.name]) {
loopables.forEach(function (loopableKey) {
res[item.name][loopableKey] += item[loopableKey];
});
}
else {
res[item.name] = item;
}
return res;
}, res)).map(function(key) {
return res[key];
});
console.log(res);
here I only rely on the main key name, the rest integer properties are automatically summed, by iterating over loopables keys array, calculated at the beginning
plunker with Angular: https://plnkr.co/edit/MRr2QRULG8TYs2CqA1By?p=preview
Upvotes: 2
Reputation: 7498
I think you can do it in pure javascript
check this following snippet
var obj = [{
"name": "Apple",
"total": 16,
"applicable": 21
}, {
"name": "Cherry",
"total": 12,
"applicable": 27
}, {
"name": "Plum",
"total": 14,
"applicable": 21
}, {
"name": "Apple",
"total": 16,
"applicable": 21
}, {
"name": "Cherry",
"total": 12,
"applicable": 27
}, {
"name": "Plum",
"total": 14,
"applicable": 21
}, {
"name": "Banana",
"total": 14,
"applicable": 21
}, ];
var newObj = [];
MergeObjectProperties(obj);
function MergeObjectProperties(obj) {
Object.keys(obj).forEach(function (key) {
var name = obj[key].name;
var exists = checkProperty(name, newObj)
if (newObj.length == 0 || !exists)
newObj.push(obj[key]);
else {
newObj[exists]["total"] = obj[key].total + newObj[exists]["total"];
newObj[exists]["applicable"] = obj[key].applicable + newObj[exists]["applicable"];
}
});
console.log(newObj);
}
function checkProperty(prop, newObj) {
var result;
Object.keys(newObj).forEach(function (key) {
if (newObj[key]["name"] === prop) {
result = key
}
});
return result;
}
Hope this helps
Upvotes: 1
Reputation: 3669
What I do to simply such things is I use angular forEach and in your case, I'd make three different arrays and fill them up with this information. Then I'd use indexof on them and push them to myNewArray. Its easier to work on simple arrays than array objects.
For example on forEach Angular forEach.
Upvotes: 1