我有一个查询语法,需要将其应用于json对象并在json对象中返回有效路径的数组。
例如,具有这样的查询:
People.[].Dependents.[]
以及以下JSON对象:
{
"People": [
{
"FirstName": "John",
"LastName": "Doe",
"Dependents": [
{
"Name": "John First Dep"
},
{
"Name": "John Second Dep"
}
]
},
{
"FirstName": "Jane",
"LastName": "Smith",
"Dependents": [
{
"Name": "Jane First Dep"
}
]
}
]
}
结果将是:
[
"People.0.Dependents.0",
"People.0.Dependents.1",
"People.1.Dependents.0",
]
我目前正试图尽可能简洁地做到这一点。到目前为止,我所做的任何尝试都导致了太多的代码,并且难以遵循。我缺少明显的东西吗?
编辑:当前代码:
function expandQuery(data, path) {
const parts = path.split("[]").map(s => _.trim(s, "."));
const [outer, ...right] = parts;
const inner = _.join(right, ".[].");
let groupData = _.get(data, outer, []);
if (!_.isArray(groupData)) {
groupData = [groupData];
}
const groupLength = groupData.length;
let items = [];
for (let ind = 0; ind < groupLength; ind++) {
items.push(outer + "." + ind.toString() + "." + inner);
}
const result = [];
for (let ind = 0; ind < items.length; ind++) {
const item = items[ind];
if (item.includes("[]")) {
result.push(...expandQuery(data, item));
} else {
result.push(_.trim(item, "."));
}
}
return result;
}
我正专门将其缩短。
答案 0 :(得分:2)
这可以满足您的要求,但是比您的解决方案简单/短得多。
function getPaths (collection, query) {
let tokens = query.split(".")
function walkPath (collection, [currentToken, ...restTokens], paths) {
if (!currentToken) { // no more tokens to follow
return paths.join(".")
}
if (currentToken === "[]") { // iterate array
const elemPaths = _.range(collection.length)
return elemPaths.map(elemPath => walkPath(collection[elemPath], restTokens, [...paths, elemPath]))
}
else {
return walkPath(collection[currentToken], restTokens, [...paths, currentToken])
}
}
return _.flattenDeep(walkPath(collection, tokens, []))
}
它也缺少错误处理。
也许这对您有用。
答案 1 :(得分:0)
要展平深度嵌套的数组,可以使用_.flattenDeep
。
但是,如果您不介意离开lodash,则可以执行以下操作:
function getNextToken(path) {
let token = path.trim();
let rest = '';
const separatorPos = path.indexOf('.');
if (separatorPos > -1) {
token = path.substr(0, separatorPos).trim();
rest = path.substr(separatorPos + 1).trim();
}
return { token, rest };
}
const expandQuery = (data, path) => {
const expand = (data, path, found = []) => {
if (data === undefined) {
return [];
}
const { token, rest } = getNextToken(path);
switch (token) {
// Got to the end of path
case '':
return [found.join('.')];
// Got an array
case '[]':
if (data.constructor !== Array) {
return [];
}
const foundPaths = [];
let i;
for (i = 0; i < data.length; ++i) {
// Flatten out foundPaths
foundPaths.push.apply(
foundPaths,
expand(data[i], rest, [...found, i])
);
}
return foundPaths;
// Got a property name
default:
return expand(data[token], rest, [...found, token]);
}
};
return expand(data, path);
};
const query = 'People.[].Dependents.[]';
const input = {
"People": [{
"FirstName": "John",
"LastName": "Doe",
"Dependents": [{
"Name": "John First Dep"
},
{
"Name": "John Second Dep"
}
]
},
{
"FirstName": "Jane",
"LastName": "Smith",
"Dependents": [{
"Name": "Jane First Dep"
}]
}
]
};
console.log(expandQuery(input, query));
可能不是最短的,但是它检查数据类型。同样null
个值也被认为是一个匹配项。如果您也想忽略它们,则可以检查是否data === null
。
答案 2 :(得分:0)
另一种方法:)
var _ = require('lodash');
var test = {
"People": [
{
"FirstName": "John",
"LastName": "Doe",
"Dependents": [
{
"Name": "John First Dep"
},
{
"Name": "John Second Dep"
}
]
},
{
"FirstName": "Jane",
"LastName": "Smith",
"Dependents": [
{
"Name": "Jane First Dep"
}
]
}
]
}
function mapper(thing, prefix, paths) {
if (_.isObject(thing)) {
_.forEach(thing, function(value, key) {mapper(value, prefix+key+'.', paths);});
} else if (_.isArray(thing)) {
for (var i = 0; i < thing.length; i++) mapper(value, prefix+i+'.', paths);
} else {
paths.push(prefix.replace(/\.$/, ''));
}
}
var query = 'People.[].Dependents.[]';
var paths = [];
var results;
query = new RegExp(query.replace(/\[\]/g,'\\d'));
mapper(test, '', paths); // Collect all paths
results = _.filter(paths, function(v) {return query.test(v);}); // Apply query
console.log(results);
答案 3 :(得分:0)
我也会试一试,请注意,当您使用_.get(object,'path.with.point')
并且对象键名中包含点时,您的代码将被破坏,我更喜欢使用_.get(object,['path','with','point'])
。
const data = {"People":[{"FirstName":"John","LastName":"Doe","Dependents":[{"Name":"John First Dep","a":[1,2]},{"Name":"John Second Dep","a":[3]}]},{"FirstName":"Jane","LastName":"Smith","Dependents":[{"Name":"Jane First Dep","a":[1]}]}]};
const flatten = arr =>
arr.reduce((result, item) => result.concat(item))
const ARRAY = {}
const getPath = (obj, path) => {
const recur = (result, path, item, index) => {
if (path.length === index) {
return result.concat(path)
}
if (item === undefined) {
throw new Error('Wrong path')
}
if (path[index] === ARRAY) {
const start = path.slice(0, index)
const end = path.slice(index + 1, path.length)
return item.map((_, i) =>
recur(result, start.concat(i).concat(end), item, index)
)
}
return recur(result, path, item[path[index]], index + 1)
}
const result = recur([], path, obj, 0)
const levels = path.filter(item => item === ARRAY).length - 1
return levels > 0
? [...new Array(levels)].reduce(flatten, result)
: result
}
console.log(
getPath(data, ['People', ARRAY, 'Dependents', ARRAY, 'a', ARRAY])
)