简体   繁体   中英

Json: sum values of json array

Below is my input:

input1 =

  [{

    "201609": 5,
    "201610": 7,
    "201611": 9,
    "201612": 10,
    "FY17": 24,
    "metric": "metric1",
    "careerLevelGroups": [{
        "201609": 3,
        "201610": 6,
        "201611": 9,
        "201612": 8
        "FY17": 18,
        "careerLevel": "Senior Managing Director",
        "careerLevels": [{
                "201609": 1,
                "201610": 2,
                "201611": 3,
                "201612": 5
                "FY17": 6,
                "careerLevel": "CL1"
            },
            {
                "201609": 2,
                "201610": 4,
                "201611": 6,
                "201612": 9
                "FY17": 12,
                "careerLevel": "CL2"
            }
        ]
    }]
}]

input2 =

   [{

        "201609": 4,
        "201610": 8,
        "201611": 12,

        "FY17": 24,
        "metric": "metric1",
        "careerLevelGroups": [{
            "201609": 9,
            "201610": 2,
            "201611": 7,
            "FY17": 18,
            "careerLevel": "Senior Managing Director",
            "careerLevels": [{
                    "201609": 3,
                    "201610": 6,
                    "201611": 9,
                    "FY17": 18,
                    "careerLevel": "CL1"
                },
                {
                    "201609": 7,
                    "201610": 8,
                    "201611": 9,
                    "FY17": 24,
                    "careerLevel": "CL2"
                }
            ]
        }]
    }]

output = input1 + input2.

My output should have the sum of all the numeric value. If it doesn't find the matching key like "201612" it should still keep that key in output.

 [{

    "201609": 9,
    "201610": 15,
    "201611": 21,
    "201612": 10,
    "FY17": 48,
    "metric": "metric1",
    "careerLevelGroups": [{
        "201609": 12,
        "201610": 8,
        "201611": 16,
        "201612": 8,
        "FY17": 24,
        "careerLevel": "Senior Managing Director",
        "careerLevels": [{
                "201609": 4,
                "201610": 8,
                "201611": 12,
                "201612": 5,
                "FY17": 24,
                "careerLevel": "CL1"
            },
            {
                "201609": 9,
                "201610": 12,
                "201611": 15,
                "201612": 9,
                "FY17": 36,
                "careerLevel": "CL2"
            }
        ]
    }]
}]

Below is what iam trying to do :

var output = [{}];

for(let i in input){    
  for (let key in input[i]){
      if (output[0].hasOwnProperty(key)) {

    output[0][key]+=input[i][key];
    }else{
    output[0][key]=input[i][key];
    }
 }
}

console.log(JSON.stringify(output)); // errors out

But this is not giving me desired result as it is not able to sum the hierarchical json structure as above.

Let's start by writing the main merge function:

function merge(x, y, fn) {

    if (isNotCompound(x) && isNotCompound(y)) {
        return fn(x, y);
    }

    if (isArray(x) && isArray(y)) {
        return mergeArray(x, y, fn);
    }

    if (isObject(x) && isObject(y)) {
        return mergeObject(x, y, fn);
    }

    throw new Error('the two input values are not of the same compound type');
} 

The merge function amounts to a dispatching on the type of its input values x and y . If they are both found to be primitive types (ie, they are not arrays nor objects) they are merged according to the fn function. Otherwise, the proper helper function is selected for proceeding with the traversing.

Let's therefore implement the type predicates necessary for the dispatch:

   const isArray = x => Array.isArray(x);
   const isObject = x => Object.prototype.toString.call(x) === '[object Object]';
   const isNotCompound = x => !isArray(x) && !isObject(x);

It is now a matter of writing mergeArray and mergeObject . One possible implementation for mergeArray is as follows:

function mergeArray(xs, ys, fn) {
    if (xs.length !== ys.length) throw new Error('the two arrays must be of the same size');
    let r = [];
    for (let i = 0; i < xs.length; i++) {
      r.push(merge(xs[i], ys[i], fn));
    }  
    return r;
}

As shown, mergeArray requires the two arrays of being of the same size. As for mergeObject , one way of implementing it is as follows:

function mergeObject(obj1, obj2, fn) {
  let r = {};
  for (let key of Object.keys(obj1)) {
    r[key] = obj2[key] ? merge(obj1[key], obj2[key], fn) : obj1[key];
  }
  for (let key of Object.keys(obj2)) {
    if (r[key]) continue;
    r[key] = obj2[key];
  }
  return r;
}

Notably, values are combined for common keys, while unshared properties are directly propagated to the resulting object.

At last, the desired merging strategy can be obtained by passing the following input function:

const isNumber = x => typeof x === 'number';
const add = (x, y) => isNumber(x) && isNumber(y) ? x + y : (x || y);

If the two input, primitive values are both numbers, it returns the sum of them, otherwise it returns whatever of the two happens to be defined.

We can finally execute:

console.log(JSON.stringify(merge(input1, input2, add)));

The whole source code follows.

 const isArray = x => Array.isArray(x); const isObject = x => Object.prototype.toString.call(x) === '[object Object]'; const isNotCompound = x => !isArray(x) && !isObject(x); function merge(x, y, fn) { if (isNotCompound(x) && isNotCompound(y)) { return fn(x, y); } if (isArray(x) && isArray(y)) { return mergeArray(x, y, fn); } if (isObject(x) && isObject(y)) { return mergeObject(x, y, fn); } throw new Error('the two input values are not of the same compound type'); }; function mergeArray(xs, ys, fn) { if (xs.length !== ys.length) throw new Error('the two arrays must be of the same size'); let r = []; for (let i = 0; i < xs.length; i++) { r.push(merge(xs[i], ys[i], fn)); } return r; } function mergeObject(obj1, obj2, fn) { let r = {}; for (let key of Object.keys(obj1)) { r[key] = obj2[key] ? merge(obj1[key], obj2[key], fn) : obj1[key]; } for (let key of Object.keys(obj2)) { if (r[key]) continue; r[key] = obj2[key]; } return r; } let input1 = [{ "201609": 5, "201610": 7, "201611": 9, "201612": 10, "FY17": 24, "metric": "metric1", "careerLevelGroups": [{ "201609": 3, "201610": 6, "201611": 9, "201612": 8, "FY17": 18, "careerLevel": "Senior Managing Director", "careerLevels": [{ "201609": 1, "201610": 2, "201611": 3, "201612": 5, "FY17": 6, "careerLevel": "CL1" }, { "201609": 2, "201610": 4, "201611": 6, "201612": 9, "FY17": 12, "careerLevel": "CL2" } ] }] }]; let input2 = [{ "201609": 4, "201610": 8, "201611": 12, "FY17": 24, "metric": "metric1", "careerLevelGroups": [{ "201609": 9, "201610": 2, "201611": 7, "FY17": 18, "careerLevel": "Senior Managing Director", "careerLevels": [{ "201609": 3, "201610": 6, "201611": 9, "FY17": 18, "careerLevel": "CL1" }, { "201609": 7, "201610": 8, "201611": 9, "FY17": 24, "careerLevel": "CL2" }] }] }]; const isNumber = x => typeof x === 'number'; const add = (x, y) => isNumber(x) && isNumber(y) ? x + y : (x || y); console.log(JSON.stringify(merge(input1, input2, add))); 

Create a recursive function and inside that use forEach to iterate the array The flow is like this instead of looping both the input1 & input2 array , loop over one of them and if the value of the key is an number then add it to the value of same key in another array.If the value of a key is an array then call the same recursive function with new arguuments

 var input1 = [{ "201609": 5, "201610": 7, "201611": 9, "201612": 10, "FY17": 24, "metric": "metric1", "careerLevelGroups": [{ "201609": 3, "201610": 6, "201611": 9, "201612": 8, "FY17": 18, "careerLevel": "Senior Managing Director", "careerLevels": [{ "201609": 1, "201610": 2, "201611": 3, "201612": 5, "FY17": 6, "careerLevel": "CL1" }, { "201609": 2, "201610": 4, "201611": 6, "201612": 9, "FY17": 12, "careerLevel": "CL2" } ] }] }] var input2 = [{ "201609": 4, "201610": 8, "201611": 12, "FY17": 24, "metric": "metric1", "careerLevelGroups": [{ "201609": 9, "201610": 2, "201611": 7, "FY17": 18, "careerLevel": "Senior Managing Director", "careerLevels": [{ "201609": 3, "201610": 6, "201611": 9, "FY17": 18, "careerLevel": "CL1" }, { "201609": 7, "201610": 8, "201611": 9, "FY17": 24, "careerLevel": "CL2" } ] }] }] // A function which will take input2 and input1 array function loopArray(arrayToBeLooped, addingArray) { // now looping over the array arrayToBeLooped.forEach(function(item, index) { // item is each object,here checking if the value of object key is array or number for (let keys in item) { // if number then find the same key from other array and add the value if (typeof item[keys] === 'number') { addingArray[index][keys] = addingArray[index][keys] + item[keys] } if (Array.isArray(item[keys])) { // if the type of value is another array for example // careerLevelGroups & careerLevels then it is basically same // as looping over array input1 & input2 // so calling the same loopArray function and passing different // parameter but the object remains same loopArray(arrayToBeLooped[index][keys], addingArray[index][keys]) } } }) } loopArray(input2, input1) console.log(input1) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM