Here is a sample of my JSON.
const data = [{
"employees": [
{
"employee": [
{
"name": "Jon",
"surname": "Smith",
"leaveRequest": [
{
"id": "3000",
"approver": "Terry"
}
]
}],
},
{
"employee": [
{
"name": "Mike",
"surname": "Jones",
"leaveRequest": [
{
"id": "1700",
"approver": "Mary"
}
]
},
]
}
]
}];
I want to be able to search by id across all leaveRequests and get the employee's name and surname.
If I pass id "3000" I would like to be able to get an object ["name": "Jon", "surname": "Smith"]
I've tried implementing the solution from: How to get immediate parent Id of the child id in array of nested json Object?
This is what I've tried:
const findEmployee = (arr, id) => {
for (let i = 0; i < arr.length; i++) {
if (arr[i].id === id) {
return [];
}
else if (arr[i].employee && arr[i].employee.length) {
const t = findEmployee(arr[i].employee, id);
if (t !== false) {
if (t.length == 0)
t.push({"name": arr[i].name, "surname": arr[i].surname});
return t;
}
}
}
return false;
};
console.log(findEmployee(data, "3000"))
But the solution in that thread relies on each child node having the same key 'children' which wouldn't work in my scenario. I'd have to restructure my JSON structure to be:
employee
employee
employee
id
And this doesn't make sense in my scenario. How can I search by id across all leaveRequests and get the employee's name and surname from the immediate parent?
I'd suggest walking through the object recursively, and returning if we find an object with a leaveRequest
property.
Once we find this we'll strip off the leaveRequest and return the rest of the object.
const data = [{ "employees": [ { "employee": [ { "name": "Jon", "surname": "Smith", "leaveRequest": [ { "id": "3000", "approver": "Terry" } ] }], }, { "employee": [ { "name": "Mike", "surname": "Jones", "leaveRequest": [ { "id": "1700", "approver": "Mary" } ] }, ] } ] }]; function findRequest(input, id) { if (input.leaveRequest && input.leaveRequest[0].id == id) { // Return everything bar the leave request... return (({ leaveRequest,...obj}) => obj)(input); } for(let k in input) { if (input[k] && typeof(input[k]) === 'object') { let leaveRequest = findRequest(input[k], id); if (leaveRequest) return leaveRequest; } } } let ids = [1700, 3000]; ids.forEach(id => console.log('id: ', id, '\nrequest:', findRequest(data, id)));
.as-console-wrapper { max-height: 100%;important }
Here is an answer using object-scan :
.as-console-wrapper {max-height: 100%;important: top 0}
<script type="module"> import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan@18.3.0/lib/index.min.js'; const data = [{ employees: [{ employee: [{ name: 'Jon', surname: 'Smith', leaveRequest: [{ id: '3000', approver: 'Terry' }] }] }, { employee: [{ name: 'Mike', surname: 'Jones', leaveRequest: [{ id: '1700', approver: 'Mary' }] }] }] }]; const find = objectScan(['[*].employees[*].employee[*].leaveRequest[*].id'], { abort: true, // abort after first result filterFn: ({ value, context }) => value === context, // find the correct id rtn: ({ parents }) => parents[2] // return the correct node }); console.log(find(data, '3000')); // => { name: 'Jon', surname: 'Smith', leaveRequest: [ { id: '3000', approver: 'Terry' } ] } </script>
Disclaimer : I'm the author of object-scan
Part of the challenge is the data is oddly shaped, with objects containing single-element arrays and arrays containing single-keyed objects. I'd start by reshaping the data to something more rational (using an assumption that the data isn't recursive -- employees don't contain other employees).
// create a flat array of employees [ employeeA, employeeB, ...
const employees = data[0].employees.map(object => object.employee[0]);
With that, the way to go from a leaveRequest
id
to the containing employee is to build an index. Here, another assumption is that leaveRequest ids are unique.
// create an index { leaveIdX: employeeC, leaveIdY: employeeD, ...
const leaveRequestIndex = employees.reduce((index, employee) => {
employee.leaveRequest.forEach(request => index[request.id] = employee);
return index;
}, {});
That provides an O(1) lookup from leaveRequest to employee, like this...
// given someLeaveRequestId, get the containingEmployee
const containingEmployee = leaveRequestIndex[someLeaveRequestId];
Testing on the OP data...
// create a flat array of employees [ { employee }, { employee }, ... const employees = data()[0].employees.map(object => object.employee[0]); // create an index { leaveId: { employee }, leaveId: { employee }, ... const leaveRequestIndex = employees.reduce((index, employee) => { employee.leaveRequest.forEach(request => index[request.id] = employee); return index; }, {}); // test it: which employee has leave request 3001? const employee = leaveRequestIndex[3001]; const { name, surname } = employee; console.log(name, surname); function data() { return [{ "employees": [ { "employee": [ { "name": "Jon", "surname": "Smith", "leaveRequest": [ { "id": "3000", "approver": "Terry" }, { "id": "3001", "approver": "Larry" } ] }], }, { "employee": [ { "name": "Mike", "surname": "Jones", "leaveRequest": [ { "id": "1700", "approver": "Mary" } ] }, ] } ] }]; }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.