Recursively create string out of array of nested objects?

Ultimately, I’m not sure if this is the right way to go about this, so I’m open to suggestions.

What I’m trying to do is write a recursive function that is called while iterating over each element of an array of objects. The function should take the array element, recursively iterate over its values and nested objects.

So, for example, the function might be called while iterating over an array of nested objects like so…

arrayOfNestedObjects.map(el => objectValues(el))

I’m using https://jsonplaceholder.typicode.com/ to fetch an array of objects of this shape…

{
  id: 10,
  name: 'Clementina DuBuque',
  username: 'Moriah.Stanton',
  email: 'Rey.Padberg@karina.biz',
  address: {
    street: 'Kattie Turnpike',
    suite: 'Suite 198',
    city: 'Lebsackbury',
    zipcode: '31428-2261',
    geo: {
      lat: '-38.2386',
      lng: '57.2232'
    }
  },
  phone: '024-648-3804',
  website: 'ambrose.net',
  company: {
    name: 'Hoeger LLC',
    catchPhrase: 'Centralized empowering task-force',
    bs: 'target end-to-end models'
  }
}

What I want to return from my recursive function is a single, concatenated string of all values for each element of the array, including all values from any nested objects, like so from the above example..

"10clementinadubuquemoriah.stantonrey.padberg@karina.bizkattieturnpikesuite198lebsackbury31428-2261-38.238657.2232024-648-3804ambrose.nethoegerllccentralizedempoweringtask-forcetargetend-to-endmodels"

The problem I’m having is that there seems to be a final call of my recursive function that returns a string omitting all values from some of the nested objects. Here’s what I have so far for my function…

function objectValues(value, ...args) {
  let string = args;
  Object.values(value).forEach((val, i) => {
    if (typeof val === 'object' && val !== null) {
      objectValues(val, string);
    } else {
      string += val
        .toString()
        .replace(/s/g, '')
        .toLowerCase();
        console.log(string);
    }
  });
  return string;
}

Logging string inside of the else clause shows me the string I want, but logging string just above the return shows me the erroneous string missing the values from the company object. It must be something simple that I’m missing about how I’ve set up the recursion. Thanks for any suggestions!

Answer

The ...args parameter is making things more complicated than they have to be. If ...args is an array, renaming it let string = args is pretty confusing – especially, as you can see later, when you do string += ... (but += doesn’t make much sense on arrays)

Instead, just declare an empty string at the beginning of the function, concatenate it when necessary, and return it at the end of the function. reduce is a bit more appropriate than forEach when you’re condensing an array into a single value, such as a string:

const value={id:10,name:'Clementina DuBuque',username:'Moriah.Stanton',email:'Rey.Padberg@karina.biz',address:{street:'Kattie Turnpike',suite:'Suite 198',city:'Lebsackbury',zipcode:'31428-2261',geo:{lat:'-38.2386',lng:'57.2232'}},phone:'024-648-3804',website:'ambrose.net',company:{name:'Hoeger LLC',catchPhrase:'Centralized empowering task-force',bs:'target end-to-end models'}}

function objectValues(value) {
  return Object.values(value).reduce((string, val, i) => string + (
    (typeof val === 'object' && val !== null)
    ? objectValues(val)
    : val
        .toString()
        .replace(/s/g, '')
        .toLowerCase()
  ));
}

console.log(objectValues(value));

Another option would be to exploit JSON.stringify‘s naturally recursive nature:

const value={id:10,name:'Clementina DuBuque',username:'Moriah.Stanton',email:'Rey.Padberg@karina.biz',address:{street:'Kattie Turnpike',suite:'Suite 198',city:'Lebsackbury',zipcode:'31428-2261',geo:{lat:'-38.2386',lng:'57.2232'}},phone:'024-648-3804',website:'ambrose.net',company:{name:'Hoeger LLC',catchPhrase:'Centralized empowering task-force',bs:'target end-to-end models'}}

function objectValues(value) {
  let string = '';
  JSON.stringify(value, (_, val) => {
    if (typeof val === 'string') string += val.replace(/s/g, '').toLowerCase();
    return val;
  });
  return string;
}

console.log(objectValues(value));