Iterate through a deeply nested object in JavaScript to update the value of a specific key on every instance

I am facing a challenge with a complex data object that includes nested objects and arrays, making it difficult to replace the value of all occurrences of link in the attributes object. The data is returned as a single large blob, and I'm struggling to come up with a flexible solution that can traverse through objects and arrays to check for the existence of link.

Let me provide an example of the data:

const data = {
  components: [
    {
      name: 'header',
      attributes: {
        messageBars: [],
        link: '/new-link/'
        navigation: {
          brand: {
            link: '/',
            alt: 'blah',
          },
        },
      },
    },
    {
      name: 'body',
      attributes: {
        header: {
          text: 'blah',
        },
        buttons: [
          {
            children: 'OK!',
            link: '/new-link/',
          },
        ],
      },
    },

I have managed to access the attributes layer but struggled with implementing a recursive function called readData.

const replaceLink = (newLink: string, data: object) => {
  data.components.forEach(component => {
     if(component.attributes) readData(component.attributes, newLink);
  });

  return data;
};

Answer №1

If you are looking to identify the existing links within a dataset, this code snippet will help you extract and display all the links present in the data.

const data = {
  components: [{
      name: 'header',
      attributes: {
        messageBars: [],
        link: '/link/',
        navigation: {
          brand: {
            link: '/',
            alt: 'blah'
          }
        }
      }
    },
    {
      name: 'body',
      attributes: {
        header: {
          text: 'blah'
        },
        buttons: [{
          children: 'OK!',
          link: '/link/'
        }]
      }
    }
  ]
}
const findLinks = (elem) => {

  if (Array.isArray(elem)) {
    elem.forEach(e => findLinks(e))
  } else if (elem instanceof Object) {
    if (elem.hasOwnProperty('link')) {
      console.log('link found', elem.link, elem)
    }
    for (const key in elem) {
      findLinks(elem[key])
    }
  }
}
findLinks(data)

Answer №2

This may seem a bit audacious, but you can transform the object structure into JSON, update the links, and then reverse the conversion:

const data = {
  components: [
    {
      name: 'header',
      attributes: {
        messageBars: [],
        link: '/newlink/',
        navigation: {
          brand: {
            link: '/',
            alt: 'blah',
          },
        },
      },
    },
    {
      name: 'body',
      attributes: {
        header: {
          text: 'blah',
        },
        buttons: [
          {
            children: 'OK!',
            link: '/newlink/',
          },
        ],
      },
    },
  ]
};



const changeLinks=newlink=>JSON.parse(JSON.stringify(data).replace(/"link":"[^"]*"/g,'"link":"'+newlink+'"'))

console.log(changeLinks("xyz"))

Answer №3

One approach to achieve this recursively entails a slightly messy code structure that gets the job done efficiently. Despite its complexity, I believe it should be relatively easy to grasp. If a value is an array, the function calls itself recursively for each item in the array. In the case of a non-array object, it replaces any value associated with the key "link", while also conducting recursive calls on all other values present. Primitive values (non-objects) remain unchanged throughout the process.

It's worth noting that this solution may not yield the desired outcome if a "link" key ever holds an object or array (as the entire object/array would be replaced instead of triggering further recursion). I assume such a scenario is unlikely, but if it were to occur, modifying the code accordingly shouldn't pose a significant challenge.

const data = {
  components: [
    {
      name: 'header',
      attributes: {
        messageBars: [],
        link: '/link/',
        navigation: {
          brand: {
            link: '/',
            alt: 'blah',
          },
        },
      },
    },
    {
      name: 'body',
      attributes: {
        header: {
          text: 'blah',
        },
        buttons: [
          {
            children: 'OK!',
            link: '/link/',
          },
        ],
      },
    },
  ]
};

const replaceLink = (newLink, value) => {
  if (Array.isArray(value)) {
    return value.map(item => replaceLink(newLink, item));
  }
  else if (value instanceof Object) {
    const replacement = { ...value };
    for (const key in replacement) {
      if (key === 'link') {
        replacement[key] = newLink;
      }
      else {
        replacement[key] = replaceLink(newLink, replacement[key]);
      }
    }
    return replacement;
  }
  return value;
};

const newData = { components: replaceLink('replacement link', data.components) };

console.log(newData);

Answer №4

Another option to achieve this is by following these steps

const example = {
  sections: [{
      title: 'Introduction',
      content: 'Lorem ipsum dolor sit amet'
    },
    {
      title: 'Features',
      items: [{
          name: 'Feature A',
          description: 'Description of Feature A'
        },
        {
          name: 'Feature B',
          description: 'Description of Feature B'
        }
      ]
    }
  ]
}

function modifyContent(newContent, object) {
  if (Array.isArray(object)) {
    object.forEach(element => {
      if (Object.prototype.toString.call(element) === '[object Object]' || Array.isArray(element)) {
        modifyContent(newContent, element);
      }
    });
  } else {
    for (element in object) {
      if (element == "content") {
        object[element] = newContent;
      }
      if (Object.prototype.toString.call(object[element]) === '[object Object]' || Array.isArray(object[element])) {
        modifyContent(newContent, object[element]);
      }
    }
  }
}

modifyContent("newContent", example);

console.log(example);

Answer №5

Separating the recursive traversal and object transformation from your specific task can be beneficial. One approach is to create a helper function called replaceVal. This function will go through an object, invoking your callback function for each nested key-value pair, thus allowing you to customize the value as needed.

You can then define a function like replaceLink according to your requirements. It's important to clarify the exact replacement process for your link. In the provided example, a callback is used to identify keys labeled as "link", evaluating if the corresponding value is a String. If so, it concatenates an uppercase version of the value with a predetermined prefix. However, this logic can be adjusted based on your specific needs.

// Helper Function
const replaceVal = (f) => (o) =>
  Array.isArray(o) 
    ? o.map(replaceVal(f))
  : Object(o) === o
    ? Object.fromEntries(Object.entries(o).map(([k, v]) => [k, replaceVal(f)(f(k, v))])
    : o

// Main Function
const replaceLink = replaceVal (
  (k, v) => k == "link" && String(v) === v ? `new/path/to${v.toUpperCase()}` : v
)

// Test Data
const data = {components: [{name: "header", attributes: {messageBars: [], link: "/link/", navigation: {brand: {link: "/", alt: "blah"}}}}, {name: "body", attributes: {header: {text: "blah"}, buttons: [{children: "OK!", link: "/link/"}]}}]}

// Demo
console.log(replaceLink(data))
.as-console-wrapper {max-height: 100% !important; top: 0}

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

Setting up a straightforward static server using node.js

var express = require('express'); var app = express(); app.use('/', express.static('./')); app.listen(80); Error message encountered when running "node server.js" in the CLI: events.js:160 throw er; // Unhandled ...

The art of handling jQuery promises and interconnected AJAX requests

Is there a better way to handle dependent AJAX calls using promises/deferred in jQuery? Currently, my code looks like this: $.when($.ajax('https://somedomain.com/getuserinfo/results.jsonp', { dataType : 'jsonp', jsonp ...

Encountering an issue where the Facebook chat plugin fails to load, resulting in an

After copying the code from Facebook and pasting it into my HTML page, I encountered an error in my console: <div id="fb-root"></div> <!-- Load Facebook SDK for JavaScript --> <script> (function(d, s, id) { var js, fjs = d.ge ...

NextJS allows for custom styling of Tiktok embeds to create a unique and

Currently, I am in the process of constructing a website that integrates Tiktok oEmbed functionality. Thus far, everything is running smoothly, but I have encountered an issue - how can I customize the styling to make the body background transparent? I ha ...

Creating a PHP array for generating a menu hierarchy

I have been searching for a solution to this problem and I have only come across solutions that directly display a menu. However, I need the menu structure in the backend for editing purposes and to efficiently learn programming, I want to avoid directly p ...

Receiving unforeseen results

Can someone please explain why the output is 3 2 15 for this code snippet? I was anticipating the output to be: 2 2 15 because First, the element a[1] which is 1 will be pre-incremented and assigned to i as 2, Then, j will also be assigned as 2 due to p ...

Using SVG graphics as data labels in a HighChart stacked column chart

I am attempting to generate a stacked column chart in Highcharts with SVG images as x-axis labels, similar to the image displayed here: https://i.stack.imgur.com/y5gL1.png I have managed to achieve this with individual data points per label (non-stacked ...

Unable to retrieve $_POST data using $.post requests while iterating through options with .each

Using the .each method, I constructed an options string and sent it via .post to an external script. clicked.closest("form").find("input,textarea").each(function(){ var input=$(this); if(index==1){ options = ...

Sending PHP array data to a JavaScript function

I am attempting to establish a connection with a database using PHP and dynamically display the results using JavaScript. Here is my approach - <?php function mainMenu($q){ $res=array();; $q->setFetchMode(PDO::FETCH_NUM); while($r = $q->fetch ...

Implementing stop loss with node-binance-api: A step-by-step guide

Currently utilizing node-binance-api for trading purposes. I have initiated an order by executing the following lines of code: let adjustLeverage = await binance.futuresLeverage(coin, 2); let res_2 = await binance.futuresMarketSell(coin, quantity); . Subs ...

The preselected value in an AngularJS select box is set to static HTML by

In my HTML, I have a select tag that I am populating using ng-repeat. <td> <select ng-model="item.data.region" style="margin-bottom: 2px;"> <option ng-repeat="region in vm.regions track by $index" value="{{region}}">{{region} ...

Set up jQuery to execute code when the document is loaded and when

I'm looking for the most effective way to combine ready and ajaxStop in jQuery. Currently, my approach involves: jQuery(document).ready(function($) { $(document).bind('ready ajaxStop', function() { $('[rel=tooltip], [data-t ...

Step-by-step guide on how to change the appearance of a <DIV> using data from a database (JSON

After retrieving data from a database as a JSON file, I have written code to loop through an item (portOn) and determine if certain ports are present in the array. If a port is found in the array, its corresponding variable is set to true. var portG01,port ...

Arrange a JSON response in descending order and filter out specific values

Currently, I'm encountering a challenge when trying to extract a specific attribute from my JSON response. The issue arises when I attempt to sort the results based on the `percentage_match` in descending order. Once sorted, my goal is to create an ar ...

Revise the mobile navigation opening feature to activate by clicking on a link instead of an icon

I've been struggling with a design issue for days now. I customized a template by editing its header and footer elements, incorporating Bootstrap 3 since the template was based on it. However, I recently discovered that Bootstrap 3 does not support su ...

Positioning a div beside another div without containing it

Check out the snippet below. I am looking to vertically center an element, 'modal', over another div, 'element', regardless of its position or margins. However, I cannot place the 'modal' div inside the 'element' di ...

The management of jQuery events through .on and .off functions, maintaining the correct order, and ensuring their

Check out this jsFiddle example: http://jsfiddle.net/fThMa/2/ By clicking inside the note or rend text fields and then double clicking any of the 4 TDs below, you can insert the appropriate HTML entities into the note or rend text fields. This also includ ...

Are the functionalities of my code implemented with Node.js and callback functions comparable to Java Threads?

I am unfamiliar with the Node.js concurrency model. Below is an example of creating Java threads and starting them concurrently. package com.main; class MyThread implements Runnable{ private int num = 0; MyThread(int num){ this.num = num; } ...

When using jQuery Ajax, the data being sent in the post request may include previous

Imagine sending the following JSON String in JsonInput First: { "JobNumber": ["208-01"], "Location": ["003118"], "HostName": "TestHOST", "WoNumber": "4268-6" } The Ajax response is succ ...

Step-by-step Guide on Initializing Materialize Dropdown Button in React

I'm currently working on this code where I need the dropdown button to function properly. Although the button appears, it doesn't work when clicked. import React, {useContext} from 'react'; import {Link, useNavigate} from 'react-ro ...