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

Processing XML Files Using Nodejs

Apologies for the rookie question, but I'm feeling a bit confused... I'm attempting to pull "objects" from an XML file so that I can modify and incorporate them into a database. I attempted using xml2js and now have a JavaScript object, but I&ap ...

Bootstrap - Keeping track of the collapse state of <div> elements after page refresh

Looking for some guidance as a javascript/jquery beginner - I am utilizing Bootstrap along with data-toggle and collapse classes to display or hide divs. I have been searching online trying to find a solution that will maintain the state of all divs, wheth ...

Steps for resetting the counter to 0 following an Ajax Refresh or Submission to the database

I have been working on a code that successfully sends multiple data to a MySQL Database using JQuery Ajax. Everything works smoothly, but I encountered an issue when trying to refresh the page with ajax and add a new record; it populates the number of time ...

Streaming HTTP content on a domain secured with HTTPS using HTML5

Is it possible to stream HTTP on an HTTPS domain without triggering browser security errors? By default, the browser tends to block such requests. I rely on the hls.js library for desktop support with .m3u8 files. When I play content directly (on mobile o ...

AngularJS $HTTP service is a built-in service that makes

I am facing an issue with pulling the list of current streams from the API and iterating that information with AngularJS. I have tried inputting the JSON data directly into the js file, and it works fine with Angular. However, when I use an http request as ...

Retrieving information within a Vue component

I am struggling to access some data I have bound to a component without success. How can I achieve this? Below is my component: export default { name: 'component-vallingby', data() { return { } }, created() {}, methods: {} } And h ...

Encountering a problem while attempting to parse a JSON object in Java using the

I am struggling with a JSON string that looks like this: String result={[{"id":"2","fullname":"Course 1"},{"id":"3","fullname":"Course 2"}]} In my Java code, I attempted to decode the JSON string using the following snippet: public class Courses { publ ...

Every time I attempt to launch my Discord bot, I encounter an error message stating "ReferenceError: client is not defined." This issue is preventing my bot from starting up successfully

My setup includes the following code: const fs = require('fs'); client.commands = a new Discord Collection(); const commandFiles = fs.readdirSync('./commands/').filter(file => file.endsWith('.js')); for(const file of com ...

Give properties to a function that is being spread inside an object

While working with React, I am facing a challenge of passing props from the instanced component into the mockFn function. The code example below is extracted and incorporates Material UI, but I am struggling on how to structure it in order to have access t ...

Having trouble with Javascript/ajax/php: data is successfully sent from server to client, but client to server communication is not working as

Apologies for the duplicate post (Admins, kindly remove the other one!). I've been receiving great assistance from you all and was hoping to seek your help once again with the following question: I am currently working on implementing AJAX by allowin ...

Import a large JSON file into either a MySQL or Oracle database

My team at work is responsible for supplying files to other services. These files typically range in size from 5MB to 500MB. We are considering using JSON instead of XML, but I am concerned about how our customers will be able to upload these files into th ...

How can we align the top edge of a div to the center of a circle within a separate div in a responsive manner?

I want to create 2 stacked divs: the first div contains a circular image, and the second div contains text. I want the second div to always cover half of the circle, with its upper edge positioned at the center point of the circle. This is my code: .cov ...

Display unique values in the array once, and display all other values

Is it possible to display only one value in a foreach loop for an array that has multiple identical values? I want to avoid grouping the array in the query like this: 0 => array (size=10) 'id' => string '1' (length=1) ...

Navbar active class not updating on jQuery page scroll

My one-page website has a fixed navbar that I want to change its active status when scrolling down to specific div positions. Even though I tried using jQuery, the code doesn't seem to work as intended. Here is the snippet: // SMOOTH SCROLLING PAGES ...

using Javascript to eliminate necessary tags from concealed tabs

My goal is to dynamically remove the required tag from fields in hidden tabs when a tab is clicked using JavaScript. The presence of the required tag on unused fields causes issues with form submission, preventing data insertion into the database. Here&apo ...

The behavior of Mozilla in handling JQuery attr() function may result in variations in design elements such as font-family or font-size

I'm currently working on the login form. Below is my view in CodeIgnitor: <div id="login-form"> <?php echo heading('Login', 2); echo form_open('login/login_user'); echo form_input('email', set_value ...

Sophisticated sinatra parameter

Creating Dynamic Surveys <script type="text/x-jsrender" id="surveyTemplate"> <li class="question"> <input type="text" name="survey[question]" /> <button class="add_answer">Add Answer</button> <ul> ...

Using the Mousetrap binding on a nuxt.js page may not be effective

Hey there! I'm trying to achieve a certain functionality where I want to redirect to a different page once a specific sequence is typed. Although I can see the message "It works" in my console, the redirection is not happening and instead, I am gettin ...

Tips for retrieving arguments within a method called from a template in vue.js?

Here is an example where I am attempting to call a method from the template and pass in some arguments. How can I access those arguments from within the method? Snippet from script: methods: { showBinaries(job, id) { let test_url = process.en ...

Can you tell me the method of checking the number of members in a voice channel?

Is there a method to determine the number of members in a voice channel or check if it's empty using discord.js (V. 13.8.1)? I attempted the following code: async function countMembers(voiceState){ let users = await voiceState.channel.members.siz ...