search nested array of objects and return full parents as results | javascript

45
April 15, 2019, at 11:40 AM

Hello I need a bit of help finishing up my function. I've successfully written a recursive function to loop through a nested object and find the results. But I'm having a hard time adding the entire parent if its children pass the test. I have the following code:

const myObj = [ 
  { 
    name: '1', 
    pages: [ 
      { 
        name: '1.1', 
        pages: [] 
      }, 
      { 
        name: '1.2', 
        pages: [] 
      }, 
    ] 
  }, 
  { 
    name: '2', 
    pages: [] 
  }, 
  { 
    name: '3', 
    pages: [] 
  } 
] 
 
function searchPages(searchQuery, obj) { 
  let searchResults = []; 
  for (let i = 0; i < obj.length; i++) { 
    let item = searchString(obj[i], searchQuery); 
 
    if (item) { 
      searchResults.push(item); 
    } 
  } 
  return searchResults; 
} 
 
function searchString(obj, string) { 
  if (obj.name.includes(string)) { 
    return obj; 
  } 
 
  for (let i = 0; i < obj.pages.length; i++) { 
    const possibleResult = searchString(obj.pages[i], string); 
    if (possibleResult) { 
      return possibleResult; 
    } 
  } 
} 
 
let searchResults = searchPages('1.1', myObj); 
console.log(searchResults);

This searches the nested array properly and gives the correct result:

  {
    "name": "1.1",
    "pages": []
  }

But I would like to return the entire parent object, instead of just the child object. So the expected result is this:

  {
    name: '1',
    pages: [
      {
        name: '1.1',
        pages: []
      },
      {
        name: '1.2',
        pages: []
      },
    ]
  }

Can anyone help me modify my function to achieve this? Thanks!

Keep in mind this is just a small object just for readability purposes. My actual object will have many more levels and properties.

Answer 1

You could take a recursive approach and check if the nested arrays have the wanted name.

function searchPages(array, string) { 
    const find = ({ name, pages }) => name.includes(string) || pages && pages.some(find); 
    return array.filter(find); 
} 
 
const 
    data = [{ name: '1', pages: [{ name: '1.1', pages: [] }, { name: '1.2', pages: [] }] }, { name: '2', pages: [] }, { name: '3', pages: [] }], 
    searchResults = searchPages(data, '1.1'); 
 
console.log(searchResults);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Answer 2

Here's one possible approach, using .filter for the top array, followed by recursive calls of .some:

const myObj = [ 
  { 
    name: '1', 
    pages: [ 
      { 
        name: '1.1', 
        pages: [] 
      }, 
      { 
        name: '1.2', 
        pages: [] 
      }, 
    ] 
  }, 
  { 
    name: '2', 
    pages: [] 
  }, 
  { 
    name: '3', 
    pages: [] 
  } 
]; 
 
const searchPages = (nameToFind, obj) => obj.filter(pageContainsName(nameToFind)); 
const pageContainsName = nameToFind => ({ name, pages }) => ( 
  name === nameToFind || pages.some(pageContainsName(nameToFind)) 
); 
let searchResults = searchPages('1.1', myObj); 
console.log(searchResults);

Answer 3

Here's my approach with .filter-ing myObj and .find-ing nested pages with given name

const myObj = [ 
  { 
    name: '1', 
    pages: [ 
      { 
        name: '1.1', 
        pages: [] 
      }, 
      { 
        name: '1.2', 
        pages: [] 
      }, 
    ] 
  }, 
  { 
    name: '2', 
    pages: [] 
  }, 
  { 
    name: '3', 
    pages: [] 
  } 
]; 
 
const searchPages = (name, arr) => arr.filter( 
  ({ pages }) => pages.find(page => page.name === name) 
)  
 
let searchResults = searchPages('1.1', myObj); 
console.log(searchResults);

Answer 4

If you want to return the parent object instead of the searched object, you just need to change the searchString() implementation so it takes the parent as third param, and then return it if you find the desired string:

function searchPages(searchQuery, obj) {
  let searchResults = [];
  for (let i = 0; i < obj.length; i++) {
    let item = searchString(obj[i], searchQuery, obj);
    if (item) {
      searchResults.push(item);
    }
  }
  return searchResults;
}
function searchString(obj, string, parent) {
  if (obj.name.includes(string)) {
    return parent;
  }
  for (let i = 0; i < obj.pages.length; i++) {
    const possibleResult = searchString(obj.pages[i], string, obj);
    if (possibleResult) {
      return possibleResult;
    }
  }
}

This way you will always take the parent into account.

Demo:

const myObj = [ 
  { 
    name: '1', 
    pages: [ 
      { 
        name: '1.1', 
        pages: [ 
          { 
            name: '1.1.1', 
            pages: [] 
          } 
        ] 
      }, 
      { 
        name: '1.2', 
        pages: [] 
      }, 
    ] 
  }, 
  { 
    name: '2', 
    pages: [] 
  }, 
  { 
    name: '3', 
    pages: [] 
  } 
] 
 
function searchPages(searchQuery, obj) { 
  let searchResults = []; 
  for (let i = 0; i < obj.length; i++) { 
    let item = searchString(obj[i], searchQuery, obj); 
 
    if (item) { 
      searchResults.push(item); 
    } 
  } 
  return searchResults; 
} 
 
function searchString(obj, string, parent) { 
  if (obj.name.includes(string)) { 
    return parent; 
  } 
 
  for (let i = 0; i < obj.pages.length; i++) { 
    const possibleResult = searchString(obj.pages[i], string, obj); 
    if (possibleResult) { 
      return possibleResult; 
    } 
  } 
} 
 
let searchResults = searchPages('1.1.1', myObj); 
console.log(searchResults);

READ ALSO
Android GridLayout with dynamic number of columns per row

Android GridLayout with dynamic number of columns per row

I have cards with different width and I would like to create a Grid Layout where width = match the parent (filling the screen) and fixed heightI would like to set the cards in the Grid layout so the number of columns changes accordingly to the width of the elements...

19
I keep getting the error &ldquo;the string did not match the expected pattern&rdquo; for my fetch request

I keep getting the error “the string did not match the expected pattern” for my fetch request

I keep getting the error "the string did not match the expected pattern" for my fetch requestsI've seen some people having similar problems on here and other forums but can't pinpoint the problem

21
Unable to pass parameter to inline javascript via ng-include

Unable to pass parameter to inline javascript via ng-include

I'm trying to build a AngularJS application with few dynamic html templates using ng-includeThese html's contain inline javascript with a variable

36