提问人:Andrei V 提问时间:9/20/2023 最后编辑:Andrei V 更新时间:9/22/2023 访问量:70
如何提取 javascript 对象的子树
How to extract subtree of the javascript object
问:
我的nodejs API服务器返回了大型JSON对象。我想让用户能够查询类似于 GraphQL 但没有架构的字段子集。API 返回的对象具有一组标准字段,但也包含用户可以添加到其“自定义”属性的任何自定义字段。我查看了 GraphQL,据我了解,它需要定义所有字段的描述符,您可以通过这些字段进行查询。不幸的是,它不起作用,因为“自定义”字段可以包含任何内容。我希望用户发送查询以及GET请求,该请求描述了我想要包含哪些字段以及要排除哪些字段。例如,如果对象具有:
{
user: {
age: 25,
weight: 170,
name: "Joe"
},
book: {
title: "Dune",
description: "Space opera",
localtion: {
planet: "Arakis",
population: 100000,
},
heroes: [
{
name: "Duke",
age: 43
},
{
name: "Paul",
age: 16
}
]
}
}
现在,如果我想在用户名和书行星名称下的所有内容,我会发送类似的东西:
{'user.name': true, 'book.location.planet': true}
但是,如果我想在书中除英雄之外的所有内容,我会发送:
{'book': true, 'book.heroes': false}
有人知道图书馆有接近它的东西吗?
编辑: 理想情况下,它应该是查询数组元素:
{'book.heroes[0]': true} // extract first element
{'book.heroes[:last].name': true} // extract only name for the last element
{'book.heroes.name': true} // extract only names for all elements
答:
1赞
Abito Prakash
9/20/2023
#1
这里有一种方法
const data={user:{age:25,weight:170,name:"Joe"},book:{title:"Dune",description:"Space opera",location:{planet:"Arakis",population:100000},heroes:[{name:"Duke",age:43},{name:"Paul",age:16},]}};
const extractData = (data, filters) => {
const [pathsToInclude, pathsToExclude] = Object.keys(filters).reduce(
(acc, keyPath) => {
acc[filters[keyPath] ? 0 : 1].push(keyPath.split("."));
return acc;
},
[[], []]
);
const result = pathsToInclude.length ? {} : { ...data };
pathsToInclude.forEach((path) => {
let current = path.shift();
let obj = result;
let dataObj = data;
while (current && obj) {
if (!path.length) {
obj[current] = dataObj?.[current];
} else {
obj[current] ??= {};
dataObj = dataObj[current];
}
obj = obj[current];
current = path.shift();
}
});
pathsToExclude.forEach((path) => {
const keyToDelete = path.pop();
let current = path.shift();
let obj = result;
while (obj[current]) {
obj = obj[current];
current = path.shift();
}
if (obj) {
delete obj[keyToDelete];
}
});
return result;
};
console.log(extractData(data, {"user.name": true,"book.location.planet": true}));
// Also works if you just want to return everything except some values
console.log(extractData(data, {"book.location": false}));
评论
0赞
Andrei V
9/20/2023
非常好的解决方案。很高兴找到允许更高级查询的库,例如按元素、索引或字段进行数组过滤,但如果不存在类似的东西,那么这对我来说是可靠的。谢谢!
0赞
Vineesh
9/20/2023
#2
您可以尝试以下逻辑。
let obj = {
user: {
age: 25,
weight: 170,
name: "Joe"
},
book: {
title: "Dune",
description: "Space opera",
location: {
planet: "Arakis",
population: 100000,
},
heroes: [
{
name: "Duke",
age: 43
},
{
name: "Paul",
age: 16
}
]
}
}
let fields1 = {'book': true, 'book.heroes': false}
let fields2 = {'user.name': true, 'book.location.planet': true}
function queryObject(obj, fields) {
let newObj = {}
Object.keys(fields).forEach(field => {
const arr = field.split('.');
if(fields[field]) {
let partialObj = JSON.parse(JSON.stringify(obj))
let addTo = newObj;
for(let i=0;i<arr.length;i++) {
if(i === arr.length-1) {
addTo[arr[i]] = partialObj[arr[i]]
} else {
if(i==0) {
newObj[arr[i]] = {}
} else {
addTo[arr[i]] = {}
}
addTo = addTo[arr[i]]
partialObj = partialObj[arr[i]]
}
}
} else {
let partialObj = newObj
for(let i=0;i<arr.length;i++) {
if(i === arr.length-1) {
delete partialObj[arr[i]]
} else {
partialObj = partialObj[arr[i]]
}
}
}
})
return newObj;
}
console.log(queryObject(obj, fields1))
console.log(queryObject(obj, fields2))
评论