【问题标题】:Modify nth nested property in object修改对象中的第 n 个嵌套属性
【发布时间】:2021-01-02 02:43:02
【问题描述】:

这基本上是适用于任何编程语言的逻辑问题,我只是碰巧使用 JavaScript 来解决这个问题

基本上想象我有某种对象,例如

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

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

听起来可能比实际复杂,但本质上 other 属性嵌套在根对象中并重复

问题是我想做一个函数,在给定的深度(n)处打破这个嵌套链,从而将所有进一步的嵌套对象设置为一个值

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

那么我怎样才能创建一个函数来接收一个包含未知数量的嵌套属性 Ruth key k 的基础对象,并使其返回一个新对象(或修改原始对象,但我更愿意只返回一个新的)几乎与基础对象相同,除了在深度索引n 处,带有键k 的嵌套属性将设置为值v,而不是继续在它的未知额外数量的链中嵌套深度

我什至无法理解如何做到这一点,我总是回到将索引k 的属性设置为等于根函数,但将属性本身作为参数传递给原始根对象(基本上是递归)除非输入对象不包含属性k,在这种情况下只返回属性本身,但这只是返回最内层嵌套属性的递归方法,但在不知所措中适合做什么为了实现上述结果,这又是一个与基础对象 Ruth 几乎相同的新对象,除了它的嵌套属性,其中键 k 在深度 n 被设置为值 v,我只是在完全不知道从哪里开始

【问题讨论】:

    标签: javascript object recursion nested logic


    【解决方案1】:

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

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

    【讨论】:

      【解决方案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)

      【讨论】:

      • 谢谢,虽然我应该提到我想保持尽可能低的水平,而不使用任何现成的方法,包括减少,只是基本的 for 循环,变量增量和函数调用基本上
      • @bluejayke 我用for...in 循环解决方案更新了我的答案
      【解决方案3】:

      这确保了深度克隆,因此原始对象不会被更改。唯一的“高级”部分是 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
      }
      

      【讨论】:

        【解决方案4】:

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

        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(否则。)

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

        【讨论】:

          猜你喜欢
          • 2020-08-02
          • 1970-01-01
          • 2021-10-27
          • 2020-11-03
          • 2020-09-12
          • 2020-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多