2016-11-25 53 views
0

我使用Firebase,他们update功能可以让用户使用以下语法多位置更新:如何获取对象中嵌套属性的keypath?

newData['users/abc/age'] = 23 
newData['users/xyz/hair'] = 'blue' 
firebase.database().ref().update(newData) 

我工作的一个实用功能,将扁平化深度嵌套的对象到像newData的对象。这涉及递归迭代对象并在循环下降时跟踪键。

到目前为止,我有这样的(它只是添加路径层,以数组现在一旦算法中是正确的,我可以简单的将.join('/')):

function getKeypaths(obj, paths = []) { 

    let keys = Object.keys(obj) 

    for (let i = 0; i < keys.length; i++) { 
     let propName = keys[i] 

     paths.push(propName) 
     if (typeof obj[propName] === "object" && typeof obj[propName] !== null) { 
      console.log("PUSHING PATH", propName, paths) 
      getKeypaths(obj[propName], paths) 
     } else { 
      console.log("val", obj[propName], paths) 

     } 
    } 
} 


let obj = { 
    a: { 
     b: { 
      c: { 
       d: 1 
      } 
     }, 
     x: { 
      y: { 
       z: 2 
      } 
     } 
    } 
} 

getKeypaths(obj) 

当我运行这一点,console.log输出看起来是这样的:

PUSHING PATH a [ 'a' ] 
PUSHING PATH b [ 'a', 'b' ] 
PUSHING PATH c [ 'a', 'b', 'c' ] 
val 1 [ 'a', 'b', 'c', 'd' ] 
PUSHING PATH x [ 'a', 'b', 'c', 'd', 'x' ] 
PUSHING PATH y [ 'a', 'b', 'c', 'd', 'x', 'y' ] 
val 2 [ 'a', 'b', 'c', 'd', 'x', 'y', 'z' ] 

正如你所看到的,循环保持前值的路径它开始挖下一个非对象的值时。输出的最后一行应改为:val 2 [ 'a', 'x', 'y', 'z' ]

我又试图清除path阵列一旦循环恢复到以前的水平:

function getKeypaths(obj, paths = []) { 
    let keys = Object.keys(obj) 
    for (let i = 0; i < keys.length; i++) { 
     let propName = keys[i] 
     if (propName) paths.push(propName) 
     if (typeof obj[propName] === "object" && typeof obj[propName] !== null) { 
      console.log("PUSHING PATH", propName, paths) 
      getKeypaths(obj[propName], paths) 
      paths.length = 0 
     } else { 
      console.log("val", obj[propName], paths) 
     } 
    } 
} 

getKeypaths(obj) 

但是,这不仅造成了环路忘记path之前其目前的水平后第一个value

PUSHING PATH a [ 'a' ] 
PUSHING PATH b [ 'a', 'b' ] 
PUSHING PATH c [ 'a', 'b', 'c' ] 
val 1 [ 'a', 'b', 'c', 'd' ] 
PUSHING PATH x [ 'x' ] 
PUSHING PATH y [ 'x', 'y' ] 
val 2 [ 'x', 'y', 'z' ] 

有什么建议吗?如果有更直接的方法可以打开助手库。

回答

1

您应该可以使用json-pointer来做你想做的事。

该模块包含一个dict函数,该函数返回包含JSON指针键及其关联值的对象。例如:

var jsonPointer = require("json-pointer"); 

var data = { 
    users: { 
     abc: { 
      age: 23 
     }, 
     xyz: { 
      hair: "blue" 
     } 
    } 
}; 
var dict = jsonPointer.dict(data); 

所得字典将是:

{ 
    "https://stackoverflow.com/users/abc/age": 23, 
    "https://stackoverflow.com/users/xyz/hair": "blue" 
} 

的键/路径将带斜杠开始,但火力地堡仍将把它们作为相对于在其update被称为参考文献

+0

一个极好的解决方案,完美的作品。如果有人需要这个类似的目的,请注意:**(a)**数组扩展到'index'上键入的'objs',即'jsonPointer.dict(['a','b','c' ])== {1:'a',2:'b',3:'c'}'(Firebase会对它们进行处理),**(b)**将会添加'null' s和'未定义'的结果''对象' - 我只是添加了一个简单的循环来删除这些值之前推到firebase,[片段在这里](https://runkit.com/brandonmp/583c60989f00610014775b5f) – Brandon

0

[我有一个工作方法,但我不打算accept这个答案,希望有人瓦特/更多的专业知识在这里可以发表意见/纠正我,如果我错了]

改变后变量,其中我从arraypath存储到string,以及一些其他的变化在OP的算法中,这个解决方案似乎工作至今:

export const flattenForUpdate = (obj, path = '') => { 
    var newData = {} 

    const _flatten = (_obj, _path) => { 
    try { 
     for (var propName in _obj) { 
      if (_obj.hasOwnProperty(propName)) { 
       if (typeof _obj[propName] === "object" 
        && typeof _obj[propName] !== null 
        && Array.isArray(_obj[propName]) === false) { 
        _flatten(_obj[propName], _path + "/" + String(propName)) 
       } else if (_obj[propName] == null) { 
        throw new Error("Null/Undefined in Firebase data update payload!") 
       } else { 
        let thisPath = _path + "/" + String(propName) 
        newData[thisPath] = _obj[propName] 
       } 
      } 
     } 
    } catch (err) { 
     console.error("ERROR IN FLATTENING", err) 
    } 
    } 
    _flatten(obj, path) 
    return newData 

} 

这是一个有点混乱,但关键的外卖店:

  • 将返回Arrays,就像它会primitive,B/C arrays在火力怪异,并不能真正支持原子颗粒的更新,所以简单的情况是覆盖当前的数组中火力
  • 将扔在null以及undefinedundefinedupdate()有效载荷将导致其防止整个写错误,并且将null删除任何值在那里,这大概是不期望
  • 它是一个封闭件(我觉得)。我在afaik之前从未使用过closures,但就我的理解而言,因为它是递归的,所以任何内部状态都必须与执行实际递归的函数保持不同的context--显然这就是为什么上帝/ ECMA /道格拉斯克罗克福德或者任何人JS包括JS。
相关问题