递归函数扩展 json 关系 JS



我有点被这个问题困住了:

我有这个 JSON:

[
{
"id": 1,
"name": "Sales",
"superdepartment": null
},
{
"id": 2,
"name": "Engineering",
"superdepartment": null
},
{
"id": 3,
"name": "Product",
"superdepartment": null
},
{
"id": 4,
"name": "Design",
"superdepartment": 3
},
{
"id": 5,
"name": "Inbound Sales",
"superdepartment": 1
},
{
"id": 6,
"name": "Outbound Sales",
"superdepartment": 1
},
{
"id": 7,
"name": "Application Security",
"superdepartment": 2
},
{
"id": 8,
"name": "Front-End",
"superdepartment": 2
},
{
"id": 9,
"name": "Sales Development",
"superdepartment": 6
},
{
"id": 10,
"name": "Product Management",
"superdepartment": 3
}
]

因此,我需要根据所需的级别递归扩展"超级部门"关系。例如:

  1. 如果我将 ?expand=superdeparment 传递给我的端点,我需要打开 1 级关系
  2. 如果我通过 ?expand=superDepartment.superDepartment,我需要打开 2 个级别,这可以继续下去,所以我认为我需要一个递归解决方案。

实际上,我有这段代码可以满足第一级,但是我在替换嵌套对象以打开第二个关系级别时遇到了几个问题。

部门.js --> 在这里,我获取数据(json(并调用"getRelations"方法。

module.exports.getAll = async function getAll(expand = null) {
let response = await data;
if (expand) {
response = modelUtils.getRelations(response, expand, response);
}
return response;
}

modelUtils.js --> 在这里,我编写了我的核心函数来实现嵌套对象:

const _ = require('lodash');
//targetEntity is de JSON that I will use to get the nested entities from my actual ID.
// In this case is the same json, but can be another different.
module.exports.getRelations = function getRelations(entity, expand, targetEntity) {
let tmpEntity = _.cloneDeep(entity);
let path = expand.split('.');
for (let i=0; i < entity.length; i++) {
tmpEntity[i] = fillRelations(entity[i], path, targetEntity);
}
return tmpEntity;
}
function fillRelations(entity, path, targetEntity, level = 0) {
let current = _.cloneDeep(entity);
const currentPath = path[level];
if (!current[currentPath]) {
return current;
}
let value = targetEntity.filter(target => target.id === current[currentPath]);
if (value.length > 0) {
current[currentPath] = value[0];
}
level++;
return fillRelations(current, path, targetEntity, level);
}

所以实际上通过这段代码并将 ?expand=superDepartment.superdepartment 传递给我的端点,我得到了这个 JSON 响应:

[
{
"id": 1,
"name": "Sales",
"superdepartment": null
},
{
"id": 2,
"name": "Engineering",
"superdepartment": null
},
{
"id": 3,
"name": "Product",
"superdepartment": null
},
{
"id": 4,
"name": "Design",
"superdepartment": {
"id": 3,
"name": "Product",
"superdepartment": null
}
},
{
"id": 5,
"name": "Inbound Sales",
"superdepartment": {
"id": 1,
"name": "Sales",
"superdepartment": null
}
},
{
"id": 6,
"name": "Outbound Sales",
"superdepartment": {
"id": 1,
"name": "Sales",
"superdepartment": null
}
},
{
"id": 7,
"name": "Application Security",
"superdepartment": {
"id": 2,
"name": "Engineering",
"superdepartment": null
}
},
{
"id": 8,
"name": "Front-End",
"superdepartment": {
"id": 2,
"name": "Engineering",
"superdepartment": null
}
},
{
"id": 9,
"name": "Sales Development",
"superdepartment": {
"id": 6,
"name": "Outbound Sales",
"superdepartment": 1
}
},
{
"id": 10,
"name": "Product Management",
"superdepartment": {
"id": 3,
"name": "Product",
"superdepartment": null
}
}
]

如您所见,ID=9 元素需要为 id=1 打开嵌套关系的第二级,因此它必须如下所示:

{
"id": 9,
"name": "Sales Development",
"superdepartment": {
"id": 6,
"name": "Outbound Sales",
"superdepartment":  {
"id": 1,
"name": "Sales",
"superdepartment": null
}
}
},

以下是针对您的问题的简单(和递归(解决方案:

const data = [{
"id": 1,
"name": "Sales",
"superdepartment": null
}, {
"id": 2,
"name": "Engineering",
"superdepartment": null
}, {
"id": 3,
"name": "Product",
"superdepartment": null
}, {
"id": 4,
"name": "Design",
"superdepartment": 3
}, {
"id": 5,
"name": "Inbound Sales",
"superdepartment": 1
}, {
"id": 6,
"name": "Outbound Sales",
"superdepartment": 1
}, {
"id": 7,
"name": "Application Security",
"superdepartment": 2
}, {
"id": 8,
"name": "Front-End",
"superdepartment": 2
}, {
"id": 9,
"name": "Sales Development",
"superdepartment": 6
}, {
"id": 10,
"name": "Product Management",
"superdepartment": 3
}
];
function compute(data, expand) {
const path = expand.split('.');
return data.map(x => attachEntities(x, data, path));
}
function attachEntities(obj, data, [prop, ...props]) {
return prop ? {
...obj,
[prop]: obj[prop] && attachEntities(data.find(y => y.id === obj[prop]) || obj[prop], data, props)
}
: obj;
}
console.log('One level', compute(data, 'superdepartment'));
console.log('Two levels', compute(data, 'superdepartment.superdepartment'));
console.log('Three levels', compute(data, 'superdepartment.superdepartment.superdepartment'));

我不太确定这如何适合上面的代码库,但我认为它解决了您正在寻找的问题:

const expand = (field, lookups, xs) => 
xs.map (x => x[field] == null ? x : {...x, [field]: lookups.find(({id}) => id == x[field])})

const expandAll = ([field, ...fields], lookups, xs) =>  
field == undefined
? xs
: fields .length > 0
? expandAll (fields, expand (field, lookups, xs), xs)
: // else
expand (field, lookups, xs)
const fillRelations = (expansionStr, xs) => 
expandAll (expansionStr .split ('.'), xs, xs)
const departments = [{ id: 1, name: "Sales", superdepartment: null }, { id: 2, name: "Engineering", superdepartment: null }, { id: 3, name: "Product", superdepartment: null }, { id: 4, name: "Design", superdepartment: 3 }, { id: 5, name: "Inbound Sales", superdepartment: 1 }, { id: 6, name: "Outbound Sales", superdepartment: 1 }, { id: 7, name: "Application Security", superdepartment: 2 }, { id: 8, name: "Front-End", superdepartment: 2 }, { id: 9, name: "Sales Development", superdepartment: 6 }, { id: 10, name: "Product Management", superdepartment: 3}]
console .log (
JSON.stringify (
fillRelations ('superdepartment.superdepartment', departments)
, null, 4)
)
.as-console-wrapper {min-height: 100% !important; top: 0}

我们定义expand,它需要一个字段名称,一个已经展开的项目数组和一个要查找的项目数组,并通过在展开列表(lookups(中查找与给定字段匹配的列表来更新最后一个列表。 我们可以这样使用:

expand('superdepartment', departments, departments)

然后我们在此基础上使用expandAll,它接受一个字段名称数组并递归调用自身和expand来填充缺失字段的详细信息。 我们可以这样使用:

expandAll(['superdepartment', 'superdepartment'], departments, departments)

最后,我们在fillRelations中提供了我们的公共 API,它通过将输入字符串拆分为数组并将我们的初始对象作为查找列表和要扩展的项目传递来启动该过程。 这具有您想要的签名:

fillRelations('superdepartment.superdepartment', departments)

我们添加了JSON.stringify调用以跳过 SO 控制台的引用字符串化。 但请注意,例如,第六个结果的第一个结果和superdepartement属性以及第九个结果的superdeparment.superdepartment属性都引用同一个对象。 我看到您正在代码中进行一些克隆,如果您不想要这些共享引用,您可以更新expand以返回对象的克隆

最新更新