Search code examples
javascriptarraysobjectrecursionnested

How to get array of strings from nested object


How to get array of strings from nested object

Based on type it is required to get array of 'link'(s) Source object:

const obj = {
  id: '01',
  options: {},
  children: [
    {
      id: '02',
      type: 'green',
      options: {
        link: 'http://some-page-023'
      },
      children: [
        {
          id: '03',
          type: 'black',
          options: {},
          children: [],
        },
        {
          id: '04',
          type: 'green',
          options: {
            link: 'http://some-page-044'
          },
          children: [
            {
              id: '05',
              type: 'white',
              options: {},
              children: [],
            }
          ],
        }
      ],
    },
    {
      id: '06',
      type: 'black',
      options: {
        link: 'http://some-page-258'
      },
      children: [
        {
          id: '07',
          type: 'green',
          options: {
            link: 'http://some-page-055'
          },
          children: [],
        },
        {
          id: '08',
          type: 'white',
          options: {},
          children: [
            {
              id: '09',
              type: 'green',
              options: {
                link: 'http://some-page-023'
              },
              children: [],
            }
          ],
        }
      ],
    },
  ]
}

What I am doing:

const a = []

const getLinks = (data, ltype) => {
   if (data.children) {
     for( let el in data.children) {
       if (data.children[el].type === ltype) {
         a.push(data.children[el].options.link)
       }
       getLinks(data.children[el], ltype)
     }
   }
   return a
 }

 const result = getLinks(obj, 'green')
 console.dir(result, { depth: null })

this works fine, result: [ 'http://some-page-023', 'http://some-page-044', 'http://some-page-055', 'http://some-page-023' ]

But I need the function to return the array of strings (array should be init and returned by function), so I need something like:

const getLinks = (data, ltype) => {
  const a = []
  function recursiveFind(children, ltype) {
    if (data.children) {
      for (let el in data.children) {
        if (data.children[el].type === ltype) {
          a.push(data.children[el].options.link)
        } else {
          recursiveFind(data.children[el], ltype)
        }
      }
    }
  }
  recursiveFind(data, ltype)
  return a
}

const result = getLinks(obj, 'green')
console.dir(result, { depth: null })

Solution

  • Your second snippet is actually fine, you just got the parameter wrong:

    const getLinks = (data, ltype) => {
        const a = []
        
        function recursiveFind(data, ltype) {
            if (data.children) {
                for (let child of data.children) {
                    if (child.type === ltype)
                        a.push(child.options.link)
                    recursiveFind(child, ltype)
                }
            }
        }
        
        recursiveFind(data, ltype)
        return a
    }
    

    Also note for..of, it's better than for..in.

    Alternatively, you can get rid of the temp array and use a generator:

    function *getLinks (data, ltype) {
        if (data.children) {
            for (let c of data.children) {
                if (c.type === ltype)
                    yield c.options.link
                yield *getLinks(c, ltype)
            }
        }
    }
    
    result = [...getLinks(obj, 'green')]