首页 > 解决方案 > 扩展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 个级别,这可以继续进行,所以我认为我需要一个递归解决方案。

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

department.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
         }
    }
},

标签: javascriptnode.jsrestrecursionexpand

解决方案


这是您问题的直接(和递归)解决方案:

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'));


推荐阅读