Manipulate JSON data using a string path in JavaScript

Suppose I have the following JSON data, which can easily be converted into a JavaScript object:

{
    "foo": {
        "bar": "Common substitute word",
        "baz": "Another common substitute word",
        "questionWords": {
            "wat": "Inadequate question word",
            "wut": "Even more inadequate question word"
        }
    }
}

Now, I receive modifications for this JSON in another JSON file, like so:

{
    "foo.questionWords.wut": "A question word to avoid"
}

The path to modify is provided as a string, and I need to update the original JSON with this new information.

However, there are cases where the new data path may not exist:

{
    "foo.callingWords.dude": "Commonly used synonym for pal"
}

Additionally, the new data path could have an unknown depth:

{
    "who.knows.how.deep.we.will.go": "Look, a penny!"
}

What would be the most effective way to handle these scenarios using plain Vanilla JavaScript without the use of any libraries?

(Feel free to utilize the latest JavaScript features.)

Thank you for your assistance!

Answer №1

Avoid using the `eval` function in situations like this one. Break down your "path" and proceed slowly:

let information = {
    "foo": {
        "bar": "Common alternative word",
        "baz": "Another widely-used substitute word",
        "questionWords": {
            "wat": "Insufficient interrogative term",
            "wut": "Even more inadequate question marker"
        }
    }
},

alterations = {
  "foo.questionWords.wut": "An undesirable question term",
  "foo.callingWords.dude": "Popular synonym for friend",
  "who.knows.how.deep.we.will.go": "Hey, look, a quarter!"
};

function implementChanges(information, alterations) {
  for (let path in alterations) {
    let key = information;
    let steps = path.split('.');
    let lastStep = steps.pop();
    steps.forEach(element => (key[element] = key[element] || {}) && (key = key[element]));
    key[lastStep] = alterations[path];
  }
  return information
}

console.log(implementChanges(information, alterations))

Answer №2

When working with vanillaJS, there is a risky option available using the eval function (but beware, proceed with caution!) as shown below:

var g = {
    "lorem": {
        "ipsum": "Random placeholder text",
        "dolor": "Another random placeholder text",
        "questionPhrases": {
            "why": "Not so great question phrase",
            "how": "Could be better question phrase"
        }
    }
};

eval("g.lorem.ipsum = 42;")

https://i.sstatic.net/hefdT.png

Answer №3

Similar to the general consensus, just steering clear of using eval(), in case it causes any issues.

let data={
    "foo": {
        "bar": "Substitute for a commonly used word",
        "baz": "Another alternative for a popular word",
        "questionWords": {
            "wat": "Not a great choice for a question word",
            "wut": "An even worse option as a question word"
        }
    }
}

let newData={
    "foo.questionWords.wut": "A question term that should be avoided"
}

for(let key in newData){
  let parts = key.split(".");
    let lastKey = parts.splice(parts.length-1,1);
    let nestedObj = parts.reduce((result, prop) => result && result[prop] ? result[prop] : '' , data);
    nestedObj[lastKey]=newData[key];
}
console.log(data);

Answer №4

Here is my recursive approach to solving the problem.

const updateNestedObject = (object, path, value) => {
  const keys = path.split(".");
  const key = keys.splice(0, 1);
  if (keys.length > 0) {
    updateNestedObject(object[key], keys.join('.'), value)
  } else {
    object[key] = value;
  }
}

const data = {
  foo: {
    bar: 11,
    baz: 12,
    bac: {
      leng: 1,
      str: 'hello world'
    }
  }
};

updateNestedObject(data, 'foo.bac.leng', 'modified');

console.log(JSON.stringify(data));

Answer №5

When utilizing an imperative approach by directly updating the source:

modifyAll = function(source, target) {
    Object.keys(target)
        .forEach((k) => modify(source, k, target[k]));
}

modify = function(source, targetKey, targetValue) {
var keys = targetKey.split('.');

    // Continue looping until there are keys to extract and as long as source exists for that key.
     while((key = keys.shift()) && source[key]) {

        // If we reach a leaf node, update it with the new value. Otherwise, keep traversing deeper into
        // the object.
        if (keys.length === 0 && typeof source[key] !== 'object') {
            source[key] = targetValue;
        } else {
            source = source[key];
        }
    }
}

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

ajax with names that are alike

I've set up a text input field that searches for file names on my server. However, it currently requires an exact match to display the file. I'm looking for a solution that can show me files even if the input text isn't an exact match. Here ...

Order of callback execution in jQuery's ready function

When two JavaScript functions on a page need to be called once the document load is complete, is there a possibility that one function could be executed before the other, or will it always be the same order? For example, using jQuery with the following co ...

Executing JavaScript - Triggering an 'onClick' event within a For loop to dynamically load multiple hyperlinks

I am currently working on creating a listview using JSON data. However, when I call an 'onclick' function from a For loop, the link opens in a new window and loads three URLs into the browser's URL input. Is there a way to modify the code be ...

Exploring the depths of deep populating in Mongo and Node.js

I am currently struggling with a complex data population issue. var commentSchema = mongoose.Schema({ name: String }); var userSchema = mongoose.Schema({ userId: { type: String, default: '' }, comments: [subSchema] }); var soci ...

What was the reason for the removal of the `encoding` keyword argument from json.loads() function in Python 3.9?

The json package's official documentation explains: json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)¶ As of version 3.6: The s parameter now supports bytes or bytear ...

I'm looking to filter this array based on the value of a subarray that the user will specify the key and value for. How can I accomplish

Given the input var key="value_0" and var input="hello", I need to filter the array in TypeScript based on these values. The filtering criteria involve checking if the array elements contain a subarray with key="value_0" and if the value of this key includ ...

Avoiding the unnecessary re-rendering of input fields in React when their values change

I am developing a form that is dynamically generated using JSON data fetched from an API. The JSON structure includes information about the input elements to be rendered, such as name, type, placeholder, validation rules, and more. { name: { elemen ...

Is there a way to automatically navigate to the accounting dashboard once the extract import is complete?

In the Odoo accounting module, you have the option to import bank statements. After the import is completed, it automatically takes you to the reconciliation view, but I am looking to redirect it to the accounting dashboard. Upon further inspection, I dis ...

Differences between Cypress and JQuery objects, and the significance of the cy.wrap function

Currently diving into cypress and experimenting with various testing methods. If anyone could lend me a hand with the following inquiries: 1. I've come to understand that most cypress objects utilize jQuery objects for DOM operations, but what sets t ...

Conceal dynamically generated div elements created with ngIf

I am currently working on initializing this div using ngOnInit in Angular ngOnInit(): void { let optTemp = ''; for (let data of arrOption) { optTemp = optTemp + '<option>' + data.trim() + '</option> ...

The footer is now accompanied by the <v-navigation-drawer> on the side

I am looking for a way to dynamically adjust the height value of a style applied to an element based on certain conditions. Specifically, when scrolling to the bottom, I want the height to be 77.5%, when the footer is not visible at all, it should be 100%, ...

Experience Markdown's Single Input Update Feature

I have recently developed a Markdown editor using Vue, similar to the examples found on Vue's website. However, my query is more related to implementation rather than Vue itself. Therefore, I am open to suggestions that do not necessarily involve Vue. ...

Tips for executing a function whenever unfamiliar anchor elements are selected in Vue.js 3

I am utilizing a div with v-html to showcase data fetched from a database: <div id="Content" v-html="${Content}"></div> Inside the presented ${Content}, there may be various a tags linking to external pages. I aim to parse ...

Begin the process by hitting the enter key

Check out my website here! I want to enhance the functionality of my site by triggering a Google Images search when the 'enter' key is pressed, instead of just relying on a button click. This is the HTML structure: <p>Enter your search t ...

Incorporating '@vite-pwa/nuxt' into a nuxt 3 project leads to hydration issues

I have a Nuxt 3 website that I want to make PWA enabled. To achieve this, I am utilizing '@vite-pwa/nuxt'. However, after adding the package and enabling PWA in my nuxt.config.ts file, I encountered two issues: Hydration errors occur when refre ...

Rotate Text in HTML <thead> Tag

I need assistance with rotating a table header. https://i.stack.imgur.com/hcvxx.png The HTML th elements within the thead section are described as follows: <th> <span class="rotate-all">Period</span> </th> <th> < ...

What is the best way to show a second button once the first button has been clicked?

Is there a way to show a second button only after the first one is clicked? I'm looking for a way to have a user click on a button, which then reveals another button. When this second button is clicked, the user should be taken back to the original b ...

Angulating Service Testing

I am encountering an issue that I'm not sure how to resolve because I am inexperienced when it comes to testing. Currently, I am testing a service that includes the following code: import { Injectable } from '@angular/core'; import { Endpo ...

Creating a basic jQuery button to switch an element's background color is a breeze

I'm new to this, so I hope this is a straightforward question. I'd like to use the bgtoggle class as a button to switch the background color of the metro class. I know I'm doing something wrong because it's not working as expected. Any ...

Changing the content of the initial post in a loop on WordPress

<?php $freeadvice=new WP_Query('category_name=freeadvice&showposts=10'); while($freeadvice->have_posts() ): $freeadvice->the_post(); ?> <li class="event"> <input type="radio" name="tl-group" /> ...