修改对象中的第n个嵌套属性

时间:2020-09-15 10:43:38

标签: javascript object recursion nested logic

这基本上是逻辑问题,应该适用于任何编程语言,我恰好为此使用了JavaScript

基本上想象我有某种类似的物体

{Props:"stuff", 
    other: {
        stuff: "etc",
        other: {
            Extra: "filler",
            other: {}
        }
    }
}

从本质上讲,是一个具有一些未知数量的额外属性的对象,但是每个子对象都有一个嵌套的属性,且名称相同(在本例中为“ other”),并且深度可能是无限的,这意味着可能存在根对象内有n个对象,每个子对象是另一个对象中容器的容器,这些对象的键是相同的(在本例中也是“ other”)

听起来可能比它复杂,但是基本上其他属性在根对象中嵌套并重复

我要成为一个函数,以给定深度(n)断开此嵌套链,从而将所有其他嵌套对象设置为一个值

这听起来似乎很复杂,但基本上,在上面的示例中,对象的“ other”属性的嵌套深度为3,假设我要在深度索引2处打破深度,从而将对象更改为具有一个嵌套的“其他”属性,设置为某个值(例如数字或字符串,例如说“ 5”),而不是设置为还包含另一个属性(以及可能的其他属性等)的另一个对象

那么我该如何制作一个函数,以接收一个包含未知数量的嵌套属性Ruth键k的基础对象,并使其返回一个新对象(或修改原始对象,但我更喜欢只是返回一个与基础对象几乎相同的新对象,只是深度索引为n时,键为k的嵌套属性将被设置为值v,而不是继续在它的嵌套深度未知数量的链中

我什至不知道该怎么做,我总是回到设置索引k等于根函数的属性,但是将属性本身作为参数传递来代替原始根对象(基本上是递归的),除非输入对象不包含属性k,在这种情况下,仅返回属性本身,但这只是一种递归方法,用于返回最内层的嵌套属性,但会造成损失适合实现上述结果的方法,这又是一个与基础对象Ruth几乎相同的新对象,但其嵌套属性的例外是深度为k的键n设置为value v,甚至从哪里开始都完全茫然

4 个答案:

答案 0 :(得分:2)

我们可以在这里使用递归函数

let obj = {Props:"stuff", 
    other: {
        stuff: "etc",
        other: {
            Extra: "filler",
            other: {
                stuff: "abc",
                other:{
                    stuff: "acbh",
                    other: {}
                }
            }
        }
    }
};

function normalizeObjectDepth(obj, level, curr_level, key){
    if(curr_level==level-1){
        obj.other = key;
        return;
    }
    normalizeObjectDepth(obj.other, level, curr_level+1, key);
}

normalizeObjectDepth(obj, 3, 0, 5);

console.log(obj);

答案 1 :(得分:2)

您可以使用reduce方法创建递归函数,该方法将检查当前递归级别是否小于目标递归级别,并在此基础上继续递归或将值设置为所需的递归级别。

const data = {
  Props: "stuff",
  other: {
    stuff: "etc",
    other: {
      Extra: "filler",
      other: {}
    }
  }
}

function modify(obj, key, lvl, val, clvl = 0) {
  return Object.entries(obj).reduce((r, [k, v]) => {
    if (k === key) {
      if (clvl < lvl) {
        r[k] = modify(v, key, lvl, val, clvl + 1)
      } else {
        r[k] = val
      }
    } else {
      r[k] = v;
    }

    return r;
  }, {})
}

const result = modify(data, 'other', 2, 'my value');
console.log(result)

具有简单for...in循环的解决方案。

const data = {
  Props: "stuff",
  other: {
    stuff: "etc",
    other: {
      Extra: "filler",
      other: {
        random: 'foo',
        other: 'random'
      }
    }
  }
}

function modify(obj, key, lvl, val, clvl = 0) {
  const result = {}

  for (let k in obj) {
    if (k === key) {
      if (clvl < lvl) {
        result[k] = modify(obj[k], key, lvl, val, clvl + 1)
      } else {
        result[k] = val
      }
    } else {
      result[k] = obj[k]
    }
  }

  return result

}

const result = modify(data, 'other', 2, 'random');
console.log(result)

答案 2 :(得分:2)

这确保了深度克隆,因此原始对象不会更改。唯一的“高级”部分是JSON.parse(JSON.stringify(...)),但是可以用任何深层复制方法代替,或者您可以在使用函数之前简单地进行深层复制

const modifyNthKey = (obj, key, value, n) => {
  let i = 0
  let newObj = JSON.parse(JSON.stringify(obj))
  let tmpObj = newObj
  while (i < n) {
    console.log('typeof: ' + typeof tmpObj[`${key}`])
    if (typeof tmpObj[`${key}`] !== 'object') {
      throw Error('unable to access key. Parent not object')
    }
    tmpObj = tmpObj[`${key}`]
    i++
  }

  tmpObj[`${key}`] = value
  return newObj
}

答案 3 :(得分:1)

这是一个相当简单的递归版本。它不会改变您的输入,但会返回一个新结构:

const modify = (obj, key, value, depth) => ({
  ...obj, 
  [key]: depth <= 0 ? value : modify (obj [key], key, value, depth - 1)
})

const data = {Props: "stuff", other: {stuff: "etc", other: {Extra: "filler", other: {}}}}

console .log (modify (data, 'other', 'new value', 2))
//~> {Props: "stuff", other: {stuff: "etc", other: {Extra: "filler", other:"new value"}}}

console .log (modify (data, 'other', 'new value', 1))
//~> {Props: "stuff", other: {stuff: "etc", other: "new value"}}

console .log (modify (data, 'other', 'new value', 0))
//~> {Props: "stuff", other: "new value"}
.as-console-wrapper {max-height: 100% !important; top: 0}

基本上,我们只是复制对象,将目标属性('other')设置为新值(如果我们位于递归的末尾,当depth为0时)或结果object[key]depth -1进行递归调用(否则)。

请注意,我们没有进行完全克隆,因此其他节点可能会通过引用共享。