Combining arbitrary number of lists inside json

Posted on

Problem

I have some JSON where each key stores an array, and I need to combine all those arrays into a single array. My current solution doesn’t seem the most friendly to review, but without forEach not able to return a value i’m struggling for a succinct solution.

config = {"levels": {"first": ["a", "b", "c"], "second": ["d", "e", "f"]}};

let combine = [];
Object.keys(config["levels"]).forEach(key=>combine.push(config["levels"][key]));
let levels = [].concat.apply([], combine);
// levels == ["a", "b", "c", "d", "e", "f"]

I never know how children config["levels"] will have there will be so this needs to be dynamic.

Solution

First

but without forEach not able to return a value i’m struggling for a succinct solution.

Addressing this in your post, in the future it may help to know you can use the .map() function, which does return values to an array, unlike forEach()

an example using .map() and what you have:

const config = {"levels": {"first": ["a", "b", "c"], "second": ["d", "e", "f"]}};
const accessor = config.levels

let combine = Object.keys(accessor).map(key=>{return accessor[key]});
let levels = [].concat.apply([], combine);

console.log(levels);
// levels == ["a", "b", "c", "d", "e", "f"]

More Succinct Solution

That said, something like this may be what you’re looking for:

const config = {"levels": {"first": ["a", "b", "c"], "second": ["d", "e", "f"]}};
const accessor = config.levels

const combine = Array.prototype.concat(...Object.values(accessor));
console.log(combine)

NOTE 1: Accessor is pulled out in case it needs to change, but is not necessary

Feedback

Is this code wrapped in a function? I ask because in the first line, i.e.

config = {"levels": {"first": ["a", "b", "c"], "second": ["d", "e", "f"]}};

config is declared outside any brackets and without any keyword like var, let or const, which leads to it being declared globally. It is wise to avoid using global variables unless you are absolutely sure you have no other way to accomplish a task. This avoids scenarios like unintentional re-assignment and tight coupling.

Also, unless a variable is re-assigned, it is wise to use const instead of let. This will prevent unintentional re-assignment. combine could be declared with const since it is never re-assigned.


Whenever an array is declared and then added to via a loop (like forEach) you should consider using the map method instead.

So the following lines

let combine = [];
 Object.keys(config["levels"]).forEach(key=>combine.push(config["levels"][key]));

Could be rewritten like this:

const combine = Object.keys(config["levels"]).map(key=>combine.push(config["levels"][key]));

Perhaps it would be helpful to go through these functional JS exercises, where you practice implementing methods like map, filter, reduce.


As was mentioned in a comment, dot notation can be used instead of bracket notation to accessing object properties. It is faster to write and clearer to read1

A Simpler Technique

One simplification would be to use Object.values() to get the nested arrays, and then use Array.flat() to join the arrays together.

const config = {"levels": {"first": ["a", "b", "c"], "second": ["d", "e", "f"]}};
const levels = Object.values(config.levels).flat();
console.log("levels: ", levels);

1https://stackoverflow.com/a/4968448/1575353

Leave a Reply

Your email address will not be published. Required fields are marked *