How to extract the values of the parent for a specific child within an array of nested JSON objects?

Take a look at this snapshot of my JSON data.

const info = [{
   "employees": [ 
      {         
         "employee": [
            {
               "name": "Jon",
               "surname": "Smith",
               "leaveRequest": [
                  {
                     "id": "3000",
                     "approver": "Terry"
                  }
               ]
            }],
      },
      {
         "employee": [
            {
               "name": "Mike",
               "surname": "Jones",
               "leaveRequest": [
                  {
                     "id": "1700",
                     "approver": "Mary"
                  }
               ]
            },
         ]
      }
   ]
}];

I'm aiming to establish a way to search by ID within all leave requests and retrieve the employee's name and surname.

For example, if I input the ID "3000", I expect to get an object with the values ["name": "Jon", "surname": "Smith"]

I attempted to apply the solution shared in: How to get immediate parent Id of the child id in array of nested json Object?

This is the implementation I tried:

const locateEmployee = (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 result = locateEmployee(arr[i].employee, id);

         if (result !== false) {
         if (result.length == 0) 
            result.push({"name": arr[i].name, "surname": arr[i].surname});
            return result;
         }
      }
   }
   return false;
};

console.log(locateEmployee(info, "3000"))

However, the approach in that discussion assumes each child node has the same key 'children', which doesn't suit my situation. I would need to reorganize my JSON structure to follow:

employee
    employee
       employee
          id

Yet, this restructuring isn't logical for my use case. How can I effectively search by ID within all leave requests and fetch the employee's name and surname from the direct parent?

Answer №1

To efficiently retrieve data, my recommendation is to recursively traverse through the object and stop when encountering an object with a leaveRequest property.

Once this property is found, remove the leaveRequest and return the remaining part of the object.

Update: This method now supports multiple leave requests per employee.

const data = [{ "employees": [ { "employee": [ { "name": "Jon", "surname": "Smith", "leaveRequest": [ { "id": "2990", "approver": "Kim" } , { "id": "3000", "approver": "Terry" } ] }], }, { "employee": [ { "name": "Mike", "surname": "Jones", "leaveRequest": [  { "id": "1200", "approver": "Jane" }, { "id": "1700", "approver": "Mary" } ] }, ] } ] }];

function findRequest(input, id) {
    if (input.leaveRequest && input.leaveRequest.find(lr => lr.id == id)) {
        // Return everything except 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; }

Answer №2

Check out this solution using object-scan:

.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module>
import objectScan from 'https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bcd3ded6d9dfc891cfdfddd2fc8d84928f928c">[email protected]</a>/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, // stop 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>

Note: The creator of object-scan is also the author of this code.

Answer №3

The data poses a challenge due to its peculiar structure, with single-element arrays within objects and single-keyed objects within arrays. One approach would be to reshape the data into a more logical format, assuming that it does not involve recursion (i.e., employees do not contain other employees).

// Generating a flat array of employees [ employeeA, employeeB, ...
const employees = data[0].employees.map(object => object.employee[0]);

To link a leaveRequest id to the corresponding employee, creating an index is key. It is assumed here that leaveRequest ids are unique.

// Creating an index { leaveIdX: employeeC, leaveIdY: employeeD, ...
const leaveRequestIndex = employees.reduce((index, employee) => {
  employee.leaveRequest.forEach(request => index[request.id] = employee);
  return index;
}, {});

This index allows for a quick lookup from leaveRequest to employee, demonstrated as follows...

// Given a particular leave request ID, find the associated employee
const associatedEmployee = leaveRequestIndex[someLeaveRequestId];

Upon testing with the given data...

// Generating a flat array of employees [ { employee }, { employee }, ...
const employees = data()[0].employees.map(object => object.employee[0]);

// Creating an index { leaveId: { employee }, leaveId: { employee }, ...
const leaveRequestIndex = employees.reduce((index, employee) => {
  employee.leaveRequest.forEach(request => index[request.id] = employee);
  return index;
}, {});

// Test scenario: Which employee submitted the leave request with ID 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"
                  }
               ]
            },
         ]
      }
   ]
}];
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using ElectronJS requires the usage of the import keyword to load ES Modules

I've recently delved into Electron development by exploring the Electron Docs. I opted for ES6 syntax with import/export, while the documentation showcased the use of require. To align with ES Module standards, I updated my package.json file with typ ...

Code snippet for calculating the size of an HTML page using JavaScript/jQuery

Does anyone know of a way to calculate and display the size/weight (in KB) of an HTML page, similar to what is done here: Page size: 403.86KB This would include all resources such as text, images, and scripts. I came across a Pelican plugin that does th ...

Converting a string into an array of JSON objects

I am currently attempting to send data to my NodeJS server using the HTTP protocol (vue-resource). The goal is to send an array of JSON objects structured like this : [{"name":"Charlotte","surname":"Chacha","birth":"2000-04-02"},{"name":"Michael","surname ...

Access Azure-Active Directory through cypress tests

Currently, I'm faced with the task of creating automated tests for an application that requires login to Azure Active Directory. These tests are being written using Cypress and TypeScript. In search of a solution, I am seeking advice on how to execute ...

Using ajax to process form submissions

I'm encountering an issue with a form that I'm using to submit data via ajax. Firebug is throwing an error "ReferenceError: editUser is not defined". The form is located within a modal and I'm intending to use it for editing user information ...

Tips for isolating data on the current page:

Currently, I am using the igx-grid component. When retrieving all data in one call and filtering while on the 3rd page, it seems to search through the entire dataset and then automatically goes back to "Page 1". Is there a way to filter data only within th ...

Does Objective C have a counterpart to the encode() method in JavaScript?

NSString *searchString = @"Lyngbø"; NSLog("%@",[searchString stringByAddingPercentEscapeUsingEncoding:NSUTF8StringEncoding]); The result is: Lyng%C3%B8 <script type="text/javascript"> document.write(escape("Lyngbø")); </script> The result ...

Incorporating jsbi into a TypeScript project while adhering to strict mode

I have been developing a TypeScript library that relies on native BigInts. While it functions perfectly in Chrome, I encountered some issues with Safari. After some research, I stumbled upon the jsbi "polyfill" that seems to solve this problem. Unfortunat ...

Enhabling Effortless Button Activation & Sustained Navigation State: Integrating Dynamic Navigation in React

"I am facing a React challenge and seeking assistance to implement a specific functionality with a button. At present, the button starts with a false state, but I intend for it to automatically activate and reveal a navigation component (nav) when the ...

javascript utilize jquery to advance saved event

When it comes to hyperlinks, I am pausing some of my native click events to verify if the resulting page contains the desired content. After storing the jquery event object and performing some validations, my goal is to allow the event to continue as usua ...

javascript path as the first argument and callback as the last argument

Currently, I am in the process of creating a wrapper function for expressjs's app.get method. In these methods such as get, you can provide the path, some options, and then the callback. However, it is possible to omit the options and still have it w ...

Initiate animation on command with either Jquery or JavaScript

I have a special notification display box that showcases errors on my website .flash { height: 75px; position: fixed; top: 20px; right: 20px; z-index: 10; background-color: #ffffff; box-shadow: 0 0 30px 2px #dddddd; -webkit-animation: flas ...

I am interested in checking the dates of the current date and disabling the button if the date falls within that range

I am attempting to deactivate a button if the current date falls within a three-month period. Despite my efforts to use a combination of Php and JavaScript, I was unable to make it work. PHP Code @php($found = false) @foreach($doctors as $doctor) ...

A function designed to detect errors based on the parameters supplied as arguments

In order to ensure secure access to my restful API, I am looking to implement an authentication function called "access". I would like the function to have the following structure whenever a user interacts with the server: access(id , token ,function(err) ...

"The challenge of achieving a transparent background with a PointMaterial texture in ThreeJS

When creating a set of particles with THREE.Points and using a THREE.PointMaterial with texture, I noticed that the transparency of the particles is working only partially. The textures are stroke rectangles created with canvas. Here is what my particles ...

Unable to retrieve observable modifications

In my code file for handling reports, named report.service.ts, I have a method that retrieves reports data. This method simply returns a constant array of report objects from a mock source. Here is the implementation: @Injectable() export class ReportServ ...

Exploring Ways to Retrieve Depth Information within three.js

I have come across shaders that can dynamically create outlines around edges based on the difference in depth of pixels. This means that pixels with less depth compared to their adjacent pixels might have a thinner outline or none at all. Examples of such ...

When working with a JavaScript array of class objects, it may appear as though the array is empty when it

Currently puzzled by the behavior of this array. Despite my efforts to find an answer, I haven't had any luck so far. I initialize var allMenuItems = []; at the top and then in getMenuItems(), I push multiple new class objects. However, when I call co ...

What is the best method for generating an inline keyboard using JSON with several InlineKeyboardButton elements on a single row?

Seeking a JSON payload to generate an inline_keyboard with multiple InlineKeyboardButton on the same row. The current code provided is functional but produces one button per row. { "telegram": { "text": "Choose a color", "reply_markup": { ...

What are the potential consequences if both jackson-jaxrs-json-provider and jersey-media-json-binding.jar are present in the class path located in the /WEB-INF/lib directory?

While working on a web application managed by Apache ant builder, we recently made an upgrade from jersey version 2.17 to 2.35 along with all the related dependencies in the classpath. However, post-upgrade, I encountered an error where the POST method arg ...