Recursively flatten a deeply-nested mix of objects and arrays
I'm trying to flatten a data object that contains mixed content (JavaScript within a React application). I just need the keys of every parent and child in the object; I don't necessarily need the values (though having them present wouldn't be a deal-breaker).
I've searched for over a week to find a solution that would fit my use case, but everything I've tried has fallen short, including vanilla JavaScript, Lodash, Underscore, and flat (NPM package).
In every case, I either get a shorter list than I expect (because I'm only getting the parents), or I get a fully-flattened object with dot notation-delimited objects, which is useless to me.
I know there are lots of questions & answers pertaining to this topic, but nothing I've seen matches my use case, and I can't seem to wrap my head around the problem.
Here is a sample of my data structure:
const sampleData = {
coyotes: '',
armadillos: false,
wombats: [''],
geckos: 0,
dogs: {
beagle: '',
bulldog: ''
},
wolves: [
{
type: '',
color: '',
range: '',
status: {
endangered: true,
protected: false
}
}
],
cats: {}
}
Here is what I (ideally) want back:
result = {coyotes, armadillos, wombats, geckos, dogs, beagle, bulldog, wolves, type, color, range, status, endangered, protected, cats}
Here is the way I'd like to call the flattening method:
flattenData(sampleData)
Here's what I have so far:
const flattenData = (obj) =>
Object.assign(
{},
...(function _flatten (o) {
return [].concat(...Object.keys(o)
.map(k =>
typeof o[k] === 'object' ? _flatten(o[k]) : (Number([k]) !== 0 && { [k]: o[k] })
)
)
}(obj))
)
...which produces:
{
"coyotes": "",
"armadillos": false,
"geckos": 0,
"beagle": "",
"bulldog": "",
"type": "",
"color": "",
"range": "",
"endangered": true,
"protected": false
}
As you'll note, some parents are missing (wombats, dogs, wolves and status), and an empty object is missing (cats).
Solution 1:
This isn't the prettiest code I've ever wrote. Unfortunately it does get everything
const flattenData = (data) => {
let results = [];
const recursive = (data) => {
Object.keys(data)
.map(function (key) {
results.push(key);
recursive(data[key]);
return data[key]; // This doesn't actually do anything...
});
}
recursive(data);
return results;
};
const value = flattenData(sampleData);