Calculating the total sum of values within a JSON array

Here is the data that I am working with:

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.

The final output should be a sum of all the numeric values. Even if a key like "201612" does not match in both inputs, it should still be included in the 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"
            }
        ]
    }]
}]

This is my approach:

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)); // encountering errors

However, this method is not providing the desired outcome as it struggles to handle the hierarchical json structure depicted above.

Answer №1

To begin, let's create the primary 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 main functionality of the merge function lies in determining the type of the input values x and y. If both are determined to be primitive types (i.e., not arrays or objects), they are merged using the provided fn function. Otherwise, specific helper functions are selected for further processing.

Next, we need to define the necessary type predicates for the dispatching process:

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

With these predicates in place, we can proceed to implement the mergeArray and mergeObject functions. The following is one possible implementation for mergeArray:

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;
}

In this implementation, mergeArray requires that the two arrays have the same length. As for mergeObject, a possible implementation could look like this:

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;
}

For common keys, property values are combined while unshared properties are directly included in the resulting object.

Finally, the desired merging behavior can be achieved by defining the input function as follows:

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

If both primitive input values are numbers, it returns their sum; otherwise, it returns whichever value is defined.

To complete the process, execute the following:

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

The full source code is presented below.

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 = [{...

...merge(input1, input2, add)));

Answer №2

Implement a recursive function that utilizes the forEach method to iterate through an array. The logic is as follows - rather than iterating over both the arrays input1 and input2, loop through one of them. If a key's value is a number, add it to the corresponding key's value in the other array. If a key's value is an array, recursively call the same function with new arguments.

var input1 = [{
  "201609": 5,
  "201610": 7,
  ...
  
  // Rest of the code removed for brevity

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to successfully call a directive function from a nested child directive

I'm facing a challenge with utilizing a directive that was created by another developer to notify my directive when the ngRepeat inside of it has completed its creation. My parent directive has an isolate scope and includes a function within its scop ...

An excellent user interface that enables the user to easily reset a field to its original default value

Within my web-based application, users have the ability to customize values in specific textboxes. If they choose not to customize a value, the system will default to a predetermined option. I am looking to incorporate a feature that allows users to easil ...

Storing client-requested data locally

Is it possible to use JavaScript to make an AJAX request to fetch data from a server, then prompt the user to save this data on their computer for later access outside of the browser session? Can this saving functionality be achieved without using a Flas ...

What is the best way to embed HTML code within an amp-list component?

I was working with a json file that looked like this: { items: [ { FullImage: "87acaed5c90d.jpg", Title: "sometext", description: "<p id="imagenews"><img src='something'></p>" , } ] } I wanted ...

Issue with Typescript and react-create-app integration using Express

I'm relatively new to Typescript and I decided to kickstart a project using create-react-app. However, I encountered an issue while trying to connect my project to a server using express. After creating a folder named src/server/server.ts, React auto ...

Modifying the attributes/properties within a class of a bootstrap button

Here is the code that I have: <b-button id="search-button" size="md" class="mt-1 w-100" type="submit" @click="someEvent()" >Example </b-button If we imagine calling someEvent() and wanting to modi ...

Encountering a TypeError while attempting to retrieve an instance of AsyncLocalStorage

In order to access the instance of AsyncLocalStorage globally across different modules in my Express application, I have implemented a Singleton class to hold the instance of ALS. However, I am wondering if there might be a more efficient way to achieve th ...

Reposition the checked box to the top of the list

My goal is to click on each item, and the selected item should move to the top of the list and be rendered at the top. However, I encountered an issue where when clicking on an item, it moves to the top but the item that replaces it also gets checked. Bel ...

I am unable to employ filtering in TypeScript

Hey there, I'm trying to filter some JSON data randomly by using this function, but I keep running into an error with my variable called filteredArray. The error message says "Property 'filter' does not exist on type 'Dispatch<SetSta ...

A guide on initiating a get request with URL in React

Utilizing React for the frontend and express for the backend has been my approach. By defining get methods in my express like this:- app.get('/addBill',(req,res)=>{ // --first method res.render("addBill"); }); app.get(&a ...

React components need to refresh after fetching data from an API

I am currently working on a React application using TypeScript and integrating JSONPlaceholder for simulating API calls. I have successfully set up everything I need, but I am encountering an issue with re-rendering components that display response data fr ...

What is the best method to close a polygon infowindow when hovering over a map marker?

I am currently working on integrating Google Maps which includes a set of polygons representing state boundaries and markers representing cities within each polygon. I want to display information when hovering over a polygon/state. My question is: how can ...

The map buttons are located underneath the map, and unfortunately, it seems that setting the map height to 100% using Angular is

Upon completing the creation and display of the map, an unusual occurrence is taking place where the map buttons ("Zoom rectangular, map settings, and scale bar") are appearing below the map as oversized icons. Additionally, there is a challenge when setti ...

PHP: A Guide to Retrieving Specific JSON Data Points

After calling an API URL, I received a JSON Response. I utilized GuzzleHttp\Client and json_decode() to handle the response. [{ "INDEX_ID": "0", "NOTES": "0; Farming" }, { "INDEX_ID": "1", "NOTES": "Fruit Name; List of Fruits;" }, { ...

React Native implementation of multiple checkboxes

Is there a way to create a multiple checkbox using the react native <CheckBox></CheckBox> component? Here is my current checkbox implementation : if(this.state.specialise_list != null){ return this.state.specialise_list.map((data, i)=>( ...

How can esbuild be used to load .wglsl files in Typescript files?

I'm currently utilizing esbuild to bundle my Typescript code, but I'm facing a challenge with configuring a loader for ".wgsl" files. Here is my app.ts file: import shader from './shader.wgsl'; //webgpu logic This is my shader.d.ts fi ...

RC6 - What is the significance of encountering an 'Unexpected token <' error message?

After updating to RC.6, I am encountering a series of errors related to third-party components. Specifically, the error message displayed is: SyntaxError: Unexpected token <. This issue has arisen with ng2-bootstrap, ng2-select, and angular2-jwt. Howev ...

Switching on and off a class in Next.js

I'm a beginner with Next.js and React framework. My question is regarding toggling a class. Here's my attempt in plain JS: function toggleNav() { var element = document.getElementById("nav"); element.classList.toggle("hidde ...

The extent of a nameless function when used as a parameter

I am currently developing a straightforward application. Whenever a user hovers over an item in the list (li), the text color changes to green, and reverts back to black when the mouse moves away. Is it possible to replace lis[i] with this keyword in the ...

Convert a single JSONObject into a JSONArray

Whenever I retrieve JSON data from my server, it can sometimes be a JSONObject (one object) or a JSONArray (multiple objects). Although I know how to identify if it is an array or an object, the issue arises when I have to implement two separate sets of lo ...