我正在使用Firebase
,他们的update
函数允许使用以下语法进行多地点更新:
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' ]
有什么建议吗?如果有更简单的方法,请打开辅助库。
答案 0 :(得分:1)
您应该可以使用json-pointer
来做您想做的事。
该模块包含一个dict
函数,该函数返回一个包含JSON指针键及其相关值的对象。例如:
var jsonPointer = require("json-pointer");
var data = {
users: {
abc: {
age: 23
},
xyz: {
hair: "blue"
}
}
};
var dict = jsonPointer.dict(data);
结果字典将是:
{
"/users/abc/age": 23,
"/users/xyz/hair": "blue"
}
键/路径将以斜杠开头,但Firebase仍将它们视为相对于调用update
的参考号。
答案 1 :(得分:0)
[我有一种工作方法,但我不会accept
这个答案,希望有更多专业知识的人可以在我这里发表意见/纠正我错了]
更改我将path
从array
存储到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
在firebase中很奇怪,并且不能真正支持原子粒度更新,所以最简单的情况是覆盖firebase中的当前数组null
以及undefined
。 undefined
有效负载中的update()
会导致错误阻止整个写入,null
会删除无论有什么价值,这可能都是不可取的closures
,但据我所知,因为它是递归的,所以任何内部状态都必须保持在与执行实际递归的函数不同的context
中 - 显然是为什么上帝/ ECMA /道格拉斯克罗克福德或任何制造JS的人都包括JS。