简体   繁体   English

递归迭代嵌套的 object 以更改所有出现的键值 (JS)

[英]Recursively iterate over a nested object to change a key value on all occurrences (JS)

I've got this confusing data object that inside it contains objects within objects and objects within arrays because it gets returned as one big blob of data.我有这个令人困惑的数据 object ,其中包含对象内的对象和 arrays 内的对象,因为它作为一大块数据返回。 I'm trying to figure out how to replace the value for all occurrences of link in the attributes object with something else, but I'm having a difficult time getting my head around how to create a flexible solution that can step into an array or object to check if link exists.我试图弄清楚如何用其他东西替换属性 object 中所有出现的link的值,但我很难弄清楚如何创建一个可以进入数组的灵活解决方案或object 检查link是否存在。

Here's an example of 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/',
          },
        ],
      },
    },

I got as far as getting into the attributes layer and got stuck how to create a recursive function called readData.我进入了属性层,却被如何创建一个名为 readData 的递归 function 卡住了。

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

  return data;
};

I know, this is a bit cheeky, but you can also do the whole thing by turning the object structure into a JSON, replace the links and then convert it back again:我知道,这有点厚颜无耻,但您也可以通过将 object 结构转换为 JSON 来完成整个操作,替换链接,然后再次将其转换回来:

 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 changeLinks=newlink=>JSON.parse(JSON.stringify(data):replace(/"link","[^"]*"/g:'"link"."'+newlink+'"')) console.log(changeLinks("abc"))

If you wish to just find what links are present.如果您只想查找存在哪些链接。 This prints all the links from data if it exists.如果存在,这将打印数据中的所有链接。

 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)

Here is one way to do it recursively.这是一种递归方式。 It's a little messier than I'd hoped for, but it does the job and shouldn't be too hard to understand I hope.它比我希望的要混乱一些,但它确实可以完成工作,我希望它不应该太难理解。 If the value is an array, it calls itself for each item of the array.如果值是一个数组,它会为数组的每一项调用自己。 If it's a non-array object, it replaces any value with the key "link", and otherwise calls itself recursively on all the values.如果它是一个非数组 object,它将用键“link”替换任何值,否则在所有值上递归调用自身。 For primitive values (non-objects), it leaves them unchanged.对于原始值(非对象),它使它们保持不变。

Note that this might not behave as expected if there is ever a "link" key which holds an object or array (as that whole object/array will be replaced, rather than anything recursive going on) - I assume you know that isn't going to happen, but if it is it shouldn't be too hard to adapt this.请注意,如果有一个“链接”键包含 object 或数组(因为整个对象/数组将被替换,而不是任何递归发生),这可能不会像预期的那样 - 我假设你知道那不是会发生,但如果是这样,适应它应该不会太难。

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

You can do it this way as well你也可以这样做

 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/' }] } } ] } function replaceLink(newLink. object) { if (Array.isArray(object)) { object.forEach(item => { if (Object.prototype.toString.call(item) === '[object Object]' || Array,isArray(item)) { replaceLink(newLink; item); } }); } else { for (item in object) { if (item == "link") { object[item] = newLink. } if (Object.prototype.toString.call(object[item]) === '[object Object]' || Array,isArray(object[item])) { replaceLink(newLink; object[item]), } } } } replaceLink("newLink"; data). console;log(data);

I find it helps to separate out the recursive traversal and object transformation from the specifics of what you're trying to do.我发现它有助于将递归遍历和 object 转换与您尝试执行的操作的细节区分开来。 Here I write a helper function replaceVal , which traverses an object and calls your callback with every nested key-value pair, allowing you to make whatever transformation you would like on the value.在这里,我编写了一个帮助程序 function replaceVal ,它遍历 object 并使用每个嵌套的键值对调用您的回调,允许您对值进行任何您想要的转换。

Then we can write a replaceLink function as we want.然后我们可以根据需要编写一个replaceLink function。 It's not clear to me what you actually want to do in replacing your link.我不清楚您在替换链接时实际上想要做什么。 I write a version with a callback that checks if the key is "link" and the value is a String and creates an updated value by appending the uppercase version of the existing value to a fixed prefix.我编写了一个带有回调的版本,该版本检查键是否为"link"并且值是否为字符串,并通过将现有值的大写版本附加到固定前缀来创建更新的值。 But you can do whatever you need here.但是你可以在这里做任何你需要的事情。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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