Filtering a deeply nested object based on an array of keys in JavaScript

I have received an object and an array. My task is to filter the object so that it only contains nodes where either the key or the value matches with the keys provided in the array. For example:

Array containing keys -

['student1', 'STU', 'LMN', 'student4']

INPUT

{
    student1: {
        name: 'ABC',
        roll: 17,
    },
    student2: {
        name: 'LMN',
        roll: 16
    },
    student3: {
        name: 'MNO',
        roll: 15
    },
    student4: {
        name: 'PQR',
        roll: 16
    }
}

OUTPUT

{
    student1: {
        name: 'ABC',
        roll: 17,
    },
    student2: {
        name: 'LMN',
        roll: 16
    },
    student4: {
        name: 'PQR',
        roll: 16
    }
}

INPUT

{
    student1: {
        name: 'ABC',
        roll: 17,
        friends: {
            student5: {},
            student6: {}
        }
    },
    student2: {
        name: 'LMN',
        roll: 16
    },
    student3: {
        name: 'MNO',
        roll: 15,
        friends: {
            student7: {
                name: 'STU'
            }
        }
    },
    student4: {
        name: 'PQR',
        roll: 16
    }
}

OUTPUT:

{
    student1: {
        name: 'ABC',
        roll: 17,
        friends: {
            student5: {},
            student6: {}
        }
    },
    student2: {
        name: 'LMN',
        roll: 16
    },
    student3: {
        name: 'MNO',
        roll: 15,
        friends: {
            student7: {
                name: 'STU'
            }
        }
    },
    student4: {
        name: 'PQR',
        roll: 16
    }
}

INPUT

{
    student5: {
        name: 'EFG',
        roll: 10,
        friends: {
            student1: {},
            student2: {}
        }
    }
}

OUTPUT:

{
    student5: {
        name: 'EFG',
        roll: 10,
        friends: {
            student1: {}
        }
    }
}

Explanation :

The keys given are

'student1', 'student4', 'STU' and 'LMN'
. Therefore, we search through the entire object and include entries where either the key or the value matches those provided keys. If a key in the object matches the keys from the array, we do not need to check its contents. For example, if 'student1' is in the keys array, we include it without checking its content.

NOTE: The object can be deeply nested as well.

MY APPROACH

I attempted to parse the object and filter out entries based on their presence, but I am not getting the correct results,

const containsKey = (obj, arr = []) => {
  return (
    obj &&
    typeof obj === "object" &&
    (arr.some((key) => key in obj) ||
      arr.indexOf(obj[targetKey]) > -1 ||
      Object.values(obj).filter((obj) => {
        return containsKey(obj, arr);
      }))
  );
};

Object.fromEntries(
    Object.entries(obj).filter(([k, v]) =>
      containsKey({ [k]: v }, arr)
    )
  );

Any assistance would be greatly appreciated. Thank you :)

Answer №1

This solution involves the utilization of three distinct functions:

  • toList: An iterative function designed to transform any given object into a flattened, one-dimensional array.
  • inList: A boolean-returning function that determines if any item from the resultant list exists within the array of keys denoted as keys
  • filObject: A method aimed at selectively 'filtering' the provided object based on the returned boolean value.

const keys = ['student1', 'STU', 'LMN', 'student4'],
      input1 =  { student1: { name: 'ABC', roll: 17, }, student2: { name: 'LMN', roll: 16 }, student3: { name: 'MNO', roll: 15 }, student4: { name: 'PQR', roll: 16 } },
      input2 = { student1: { name: 'ABC', roll: 17, friends: { student5: {}, student6: {} } }, student2: { name: 'LMN', roll: 16 }, student3: { name: 'MNO', roll: 15, friends: { student7: { name: 'STU' } } }, student4: { name: 'PQR', roll: 16 } },
      
      toList = o => Object.entries(o).flat().map(
          v =>
          typeof(v) === 'object' && !Array.isArray(v) ?
              toList(v) :
              v
      )
      .flat(),
      inList = (list, o) => toList(o).some(k => list.includes(k)),
      
      filObject = (list,obj) => Object.entries(obj).reduce(
          (acc,[key,value]) => 
          inList(list,{[key]:value}) ? {...acc,[key]:value} : acc, {}
      );
      
      
console.log( filObject(keys,input1) );

console.log( filObject(keys,input2) );

Update Information ...

The modified version of the filObject function here employs recursive strategies for delving deeper into sections where the test fails for the key yet remains successful for the value. The original line

inList(list,{[key]:value}) ? {...acc,[key]:value} : acc, {}
has been substituted with
list.includes(key) ? {...acc,[key]:value} : inList(list,value) ? {...acc,[key]:filObject(list,value)} : acc, {}
.

const keys = ['student1', 'STU', 'LMN', 'student4'],
      input1 =  { student1: { name: 'ABC', roll: 17, }, student2: { name: 'LMN', roll: 16 }, student3: { name: 'MNO', roll: 15 }, student4: { name: 'PQR', roll: 16 } },
      input2 = { student1: { name: 'ABC', roll: 17, friends: { student5: {}, student6: {} } }, student2: { name: 'LMN', roll: 16 }, student3: { name: 'MNO', roll: 15, friends: { student7: { name: 'STU' } } }, student4: { name: 'PQR', roll: 16 } },
      input3 = { student5: { name: 'EFG', roll: 10, friends: { student1: {}, student2: {} } } },
      
      toList = o => Object.entries(o).flat().map(
          v =>
          typeof(v) === 'object' && !Array.isArray(v) ?
              toList(v) :
              v
      )
      .flat(),
      inList = (list, o) => toList(o).some(k => list.includes(k)),
      
      filObject = (list,obj) => Object.entries(obj).reduce(
          (acc,[key,value]) => 
          [key,value].some(p => list.includes(p)) ? {...acc,[key]:value} :
          inList(list,value) ? {...acc,[key]:filObject(list,value)} : acc, {}
      );


console.log( filObject(keys,input1) );

console.log( filObject(keys,input2) );

console.log( filObject(keys,input3) );

Answer №2

To efficiently search for specific keys and values within nested objects, you can utilize a recursive function that iterates through the object using Object.entries(object). The function will check if either the key or value of each entry exists in the keys array. If a value is another object, the function will recursively call itself to search deeper.

function searchNested(object, keys) {
    for(const [key, value] of Object.entries(object)){
        if(
            (keys.includes(key) || keys.includes(value)) 
          &&
          (value != null && typeof value == 'object' && searchNested(value, keys))
        ) {
            return true;
        }
    }
    return false;
}

function filterByKeys(object, keys) {
    const result = {};

    for(const [key, value] of Object.entries(object)){
        if(
            (keys.includes(key) || keys.includes(value)) 
          &&
          (value != null && typeof value == 'object' && searchNested(value, keys))
        ) {
            result[key] = value;
        }
    }

    return result;
}

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

HAproxy: unique error handling for OPTIONS and POST requests with 503 errorfile

Our Web application utilizes ajax calls to a backend that operates on a different domain, requiring CORS. The backend setup includes an HAproxy 1.4.22 along with multiple Wildflys running on the OpenShift PaaS. During times when a Wildfly instance is unava ...

Alter the color of the text within the `<li>` element when it is clicked on

Here is a list of variables and functions: <ul id="list"> <li id="g_commondata" value="g_commondata.html"> <a onclick="setPictureFileName(document.getElementById('g_commondata').getAttribute('value'))">Variable : ...

The Tailwind style did not completely translate when applied to the content of dangerouslySetInnerHtml

I have an HTML file containing Tailwind styles stored in my database, which needs to be fetched first. This will result in an HTML string that will be inserted into dangerouslySetInnerHtml. Here is the code snippet (utilizing Qwik): export const useHTML = ...

Navigating through Next.js for slug URLs such as site.com/username

Can someone help me figure out how to create a profile page for each username, like site.com/jack or site.com/jill? I'll be pulling the username info from an API that also contains the user ID and email. I'm new to Next.js and would really appre ...

Invoking a Components function from a Service in Angular may lead to a potential cyclic dependency issue

I am facing a challenge where I need to call a function from my filterComponent(component) within my engagementService(service). The function in my filterComponent accesses an array that is located within the engagementService. It uses the data from this ...

Using a conditional statement in a JavaScript loop

Here is the data I have: companyList: [ { company_name: 'company1', item: 'item1' }, { company_name: 'company1', item: 'item2' }, { company_n ...

Tips on selecting the active color ID from a list of available color IDs

Currently, I am trying to retrieve the color ID of the active color selection. For example, if I have three colors - yellow, blue, and red - with yellow being the default color. In this scenario, I can obtain the color ID of yellow using a hidden input typ ...

Generating dynamic strings for identifiers in JSX

Can you help me figure out how to dynamically create IDs like this? <div id="track_1"></div> <div id="track_2"></div> I tried assigning the IDs from the parent component like this: export default function Compon ...

Pressing the Like Button triggers the transfer of a specific variable from PHP to jQuery

I have a piece of PHP code that I am using to display all users' posts, each with its own unique 'like' button: $sql = "SELECT * FROM posts"; $result = mysqli_query($con,$sql); while($row=mysqli_fetch_assoc($result)){ $post_content = $ro ...

Unable to scroll to the top of the page with JavaScript

I tried the code below but it didn't quite do the trick. Can someone assist me with refreshing the page to the top every time it loads? window.addEventListener('load', function(){ window.scrollTo(0,0) }) window.onload = (event) => { ...

How can we nest a div within the outermost parent element?

Consider a scenario where there are 3 different divs named grandParent, Parent, and Child. Although the parent's tag is placed inside the grandParent, due to the use of position: absolute; property, the parent ends up being displayed outside the grand ...

Guide on How to Retrieve the Following YouTube Video using a PHP Array

I have a PHP/HTML script that currently loads a random YouTube video from an array every time the page is refreshed. However, I am looking to add functionality for next and previous buttons to allow users to cycle through the videos in the array. The goal ...

Can an in-progress NPM package be tested using NPX?

I am currently developing an NPM package that is designed to be used exclusively through npx *, similar to packages like create-nuxt-app. Is there a method to test my package using npx *? Essentially, I want to run my bin script without actually installin ...

The code for the Express app.get() method on the server is not being updated, although other parts of the

Recently, I encountered an issue with my node js and express server that is running on localhost. I made a change in the server code from: app.get('/', (req, res) => { res.sendFile(__dirname + '/public/index.html'); }); To: app. ...

Pass a JavaScript array variable to a PHP script utilizing jQuery Ajax and receive a string output instead of an array

Whenever I attempt to transmit a JavaScript array to a PHP script via Ajax, the output of $_POST['file_paths'] when var_dumped shows up as a string instead of an array. My goal is to have the output in array format rather than in string format. C ...

Determination of Array Size using Powershell

Having some trouble error trapping an empty array in the code below. Any suggestions on how to correct the syntax? $inputstring = "MyOtherFile.rdl" "MyFile.rdl" $cleanstring = $inputstring.replace(""" """,";") $filearray = $inputstring.split(";") if ( ...

Combining platform-express and platform-fastify for optimal performance

I am currently working on a NestJS application and my goal is to upload files using the package @types/multer. However, I encountered an issue while following the guidelines from the official documentation: Upon starting my application development, I dec ...

Anomaly in Date String Comparison within Angular Controller

Having a puzzling issue when attempting to compare two date strings within my Angular Controller. It seems that the comparison is yielding unexpected results. To explain, I first convert today's date to a string ("2/5/2016") and then proceed to use it ...

transferring data from a web page to a php script seamlessly without navigating away

I've already spent a significant amount of time searching on Stack Overflow and Google, but I haven't found a solution that works for me. My issue is that I have an HTML form and I need to send the data to PHP without redirecting or leaving the ...

The integration of Zoom SDK with React and Node inevitably leads to encountering errors

I have been struggling to integrate zoomsdk into my react app and have followed all the steps mentioned here. The backend part is functioning properly, and I am receiving the signature response. However, when attempting to run the frontend portion, I encou ...