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

时间:2020-08-11 19:44:08

标签: javascript arrays object recursion

我有一个令人困惑的数据对象,它里面包含对象和数组内的对象,因为它作为一个大数据块返回。我试图找出如何用其他方法替换属性对象中所有link出现的值,但是我很难理解如何创建可以逐步解决的灵活解决方案放入数组或对象以检查link是否存在。

以下是数据示例:

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/',
          },
        ],
      },
    },

我已经深入到属性层,并且陷入了如何创建称为readData的递归函数的问题。

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

  return data;
};

5 个答案:

答案 0 :(得分:2)

我知道,这有点厚脸皮,但是您也可以通过将对象结构转换为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"))

答案 1 :(得分:2)

如果您只想找到存在的链接。这会打印出数据中的所有链接。

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)

答案 2 :(得分:1)

这是递归执行此操作的一种方法。它比我希望的要混乱一些,但是它确实可以完成工作,我希望它理解起来也不会太难。如果值是一个数组,它将为数组的每个项目调用自身。如果它是一个非数组对象,它将用键“ link”替换任何值,否则将对所有值进行递归调用。对于原始值(非对象),它们保持不变。

请注意,如果有一个保存对象或数组的“链接”键(这将替换整个对象/数组,而不是任何递归的内容),则此行为可能不会达到预期的效果-我假设您知道不会发生,但是如果是这样的话,适应它应该不难。

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

答案 3 :(得分:1)

您也可以这样做

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

答案 4 :(得分:0)

我发现将递归遍历和对象转换与您要执行的操作的细节区分开来是有帮助的。在这里,我编写了一个辅助函数replaceVal,该函数遍历一个对象并使用每个嵌套的键/值对调用回调,从而使您可以对值进行所需的任何转换。

然后,我们可以根据需要编写一个replaceLink函数。对我来说,您替换链接实际上要做什么并不清楚。我写了一个带有回调的版本,该回调检查键是否为"link"且值是否为String,并通过将现有值的大写版本附加到固定前缀来创建更新后的值。但是您可以在这里做任何您需要的事情。

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

相关问题