Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find all values by specific key in a deep nested object

How would I find all values by specific key in a deep nested object?

For example, if I have an object like this:

const myObj = {
  id: 1,
  children: [
    {
      id: 2,
      children: [
        {
          id: 3
        }
      ]
    },
    {
      id: 4,
      children: [
        {
          id: 5,
          children: [
            {
              id: 6,
              children: [
                {
                  id: 7,
                }
              ]
            }
          ]
        }
      ]
    },
  ]
}

How would I get an array of all values throughout all nests of this obj by the key of id.

Note: children is a consistent name, and id's won't exist outside of a children object.

So from the obj, I would like to produce an array like this:

const idArray = [1, 2, 3, 4, 5, 6, 7]
like image 774
cup_of Avatar asked Feb 24 '19 22:02

cup_of


5 Answers

This is a bit late but for anyone else finding this, here is a clean, generic recursive function:

function findAllByKey(obj, keyToFind) {
  return Object.entries(obj)
    .reduce((acc, [key, value]) => (key === keyToFind)
      ? acc.concat(value)
      : (typeof value === 'object')
      ? acc.concat(findAllByKey(value, keyToFind))
      : acc
    , [])
}

// USAGE
findAllByKey(myObj, 'id')
like image 168
Steve Holgado Avatar answered Nov 15 '22 16:11

Steve Holgado


let str = JSON.stringify(myObj);
let array = str.match(/\d+/g).map(v => v * 1);
console.log(array); // [1, 2, 3, 4, 5, 6, 7]
like image 27
SUDHIR KUMAR Avatar answered Oct 05 '22 08:10

SUDHIR KUMAR


You could make a recursive function like this:

idArray = []

function func(obj) {
  idArray.push(obj.id)
  if (!obj.children) {
    return
  }

  obj.children.forEach(child => func(child))
}

Snippet for your sample:

const myObj = {
  id: 1,
  children: [{
      id: 2,
      children: [{
        id: 3
      }]
    },
    {
      id: 4,
      children: [{
        id: 5,
        children: [{
          id: 6,
          children: [{
            id: 7,
          }]
        }]
      }]
    },
  ]
}

idArray = []

function func(obj) {
  idArray.push(obj.id)
  if (!obj.children) {
    return
  }

  obj.children.forEach(child => func(child))
}

func(myObj)
console.log(idArray)
like image 11
zmag Avatar answered Nov 15 '22 16:11

zmag


I found steve's answer to be most suited for my needs in extrapolating this out and creating a general recursive function. That said, I encountered issues when dealing with nulls and undefined values, so I extended the condition to accommodate for this. This approach uses:

Array.reduce() - It uses an accumulator function which appends the value's onto the result array. It also splits each object into it's key:value pair which allows you to take the following steps:

  1. Have you've found the key? If so, add it to the array;
  2. If not, have I found an object with values? If so, the key is possibly within there. Keep digging by calling the function on this object and append the result onto the result array; and
  3. Finally, if this is not an object, return the result array unchanged.

Hope it helps!

const myObj = {
  id: 1,
  children: [{
      id: 2,
      children: [{
        id: 3
      }]
    },
    {
      id: 4,
      children: [{
        id: 5,
        children: [{
          id: 6,
          children: [{
            id: 7,
          }]
        }]
      }]
    },
  ]
}

function findAllByKey(obj, keyToFind) {
  return Object.entries(obj)
    .reduce((acc, [key, value]) => (key === keyToFind)
      ? acc.concat(value)
      : (typeof value === 'object' && value)
      ? acc.concat(findAllByKey(value, keyToFind))
      : acc
    , []) || [];
}

const ids = findAllByKey(myObj, 'id');

console.log(ids)
like image 7
RaaaCode Avatar answered Nov 15 '22 16:11

RaaaCode


You can make a generic recursive function that works with any property and any object.

This uses Object.entries(), Object.keys(), Array.reduce(), Array.isArray(), Array.map() and Array.flat().

The stopping condition is when the object passed in is empty:

const myObj = {
  id: 1,
  anyProp: [{
    id: 2,
    thing: { a: 1, id: 10 },
    children: [{ id: 3 }]
  }, {
    id: 4,
    children: [{
      id: 5,
      children: [{
        id: 6,
        children: [{ id: 7 }]
      }]
    }]
  }]
};

const getValues = prop => obj => {
  if (!Object.keys(obj).length) { return []; }

  return Object.entries(obj).reduce((acc, [key, val]) => {
    if (key === prop) {
      acc.push(val);
    } else {
      acc.push(Array.isArray(val) ? val.map(getIds).flat() : getIds(val));
    }
    return acc.flat();
  }, []);
}

const getIds = getValues('id');

console.log(getIds(myObj));
like image 6
jo_va Avatar answered Nov 15 '22 17:11

jo_va