The key to getting all the keys from an object of unknown size is recursion. Here I have 2 solutions; one Functional and one Object Oriented. Also I created some other data to test with in addition to yours.
Here is the repo with the complete code: https://github.com/vasilionjea/json-keys
The Data:
var data = {
continents: {
europe: {
countries: [
{
'country-name': 'italy',
cities: [
{ 'city-name': 'Rome', title: 'Roma Dolor', population:873, gdb: 301 },
{ 'city-name': 'Venice', title: 'Venice Veggies.', population:456, gdb: 244 }
]
},
{
'country-name': 'france',
cities: [
{ 'city-name': 'Paris', title: 'De Ipsum', population:7123, gdb: 77 },
{ 'city-name': 'Marseille', title: 'La Mipsum', population:73, gdb: 7 }
]
}
]
},
'north-america': {
countries: [
{
'country-name': 'canada',
cities: [
{ 'city-name': 'Montreal', title: 'The city of lorem ipsum!', population:99, gdb: 011 },
{ 'city-name': 'Quebec', title: 'Veggie Ipsum.', population:123, gdb: 101 }
]
},
{
'country-name': 'usa',
cities: [
{ 'city-name': 'New York', title: 'Empire State', population:1001001, gdb: 1010 },
{ 'city-name': 'San Francisco', title: 'SF Bridge', population:20123, gdb: 202 }
]
}
]
}
}
}
The functional way:
/*
* Functional example.
* Retrieves all keys from an object of unknown size using recursion.
*/
function _isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
function _eachKey(obj, callback) {
Object.keys(obj).forEach(callback);
}
var _container = [];
function collectKeys(data) {
_eachKey(data, function(key) {
_container.push(key);
if (_isObject(data[key])) {
collectKeys(data[key]);
}
if (Array.isArray(data[key])) {
// Because we're looping through an array and each array's item is an object,
// the `collectKeys` function is receiving each object of the array as an argument.
data[key].forEach(collectKeys);
}
});
return _container;
}
// Execute the function
var myKeys = collectKeys(data);
The Object Oriented way:
/*
* Object Oriented example.
* Retrieves all keys from an object of unknown size using recursion.
*/
function JSONKeys(obj) {
this._container = [];
if (this._isObject(obj)) {
// Just a normal object literal.
this._data = obj;
} else if (typeof obj === 'string') {
// Just in case the data was passed in as a valid JSON string.
this._data = JSON.parse(obj);
} else {
throw new Error('The provided argument must be an object literal or a JSON string.');
}
}
JSONKeys.prototype = {
constructor: JSONKeys,
_isObject: function(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
},
_eachKey: function(obj, callback) {
// Using `bind(this)` makes sure that the `this` value in the `_collect` method
// isn't set to the global object (window).
Object.keys(obj).forEach(callback.bind(this));
},
_recur: function(key, data) {
// If this key's value is also an object go deeper.
if (this._isObject(data[key])) {
this._collect(data[key]);
}
if (Array.isArray(data[key])) {
// * Because we're looping through an array and each array's item is an object,
// the `_collect` method is receiving each object of the array as an argument.
// * Using `bind(this)` makes sure that the `this` value in the `_collect` method
// isn't set to the global object (window).
data[key].forEach(this._collect.bind(this));
}
},
_collect: function(data) {
this._eachKey(data, function(key) {
this._container.push(key);
// Continue collecting
this._recur(key, data);
});
},
getAll: function() {
this._collect(this._data);
return this._container;
}
};
// Create a new instance passing the data.
var keysObject = new JSONKeys(data);
allKeys = keysObject.getAll();