0

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?

5
  • 1
    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. Commented Oct 26, 2016 at 19:35
  • @ThatBird wish I could do that, unfortunately, this is just a simple version of what I'm trying to accomplish. So with this example, three arrays would work, but with a bigger data set, it would not. Plus, with your suggested approach, this wouldn't be dynamic. but rather hardcoded Commented Oct 26, 2016 at 19:38
  • Well, I've worked on some 50 thousand values(mostly more than this) with this method and I never ran into any trouble. Although yes, you'd have to know beforehand what you're going to recieve in your data object(myArray in your case) Commented Oct 26, 2016 at 19:44
  • @thatbird, you're right :) might have to go with this solution Commented Oct 26, 2016 at 19:46
  • I'll post this as an answer too, let me know if you run into some trouble ! Commented Oct 26, 2016 at 19:51

3 Answers 3

2

try this approach:

  1. create object where keys are name property and total along with applicable are already calculated (Array.prototype.reduce)
  2. Iterate over keys of previously created object and transform it back to array (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

Sign up to request clarification or add additional context in comments.

2 Comments

This is badass, but can you explain how the above code would work if the array of objects was not explicitly stated? Meaning, instead of the array being hard-coded out, it's already set as a variable? say for example, $scope.myArray. I ask because you have the reduce function mid array declaration.
updated my answer with another snippet, where array is in the variable, and summed values are calculated programmatically
1

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.

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.