Reputation: 602
I'm translating some code from Python to JavaScript and I need to rewrite the following nested for-loop list comprehension (where solution
is a dictionary of string keys and list values).
events = [e for key in solution.keys() for c in solution[key] for e in c.events]
Without giving it much thought, I'd translate it to something like this unwieldy, ugly nested for-loop.
const events = []
for (const key of solution.keys()) {
for (const c of solution[key]) {
for (const e of c.events) {
events.push(e)
}
}
}
But maybe there's a nicer way. How can I rewrite the above nested for-loop in short, idiomatic, modern (ES2015+) JavaScript?
Upvotes: 0
Views: 506
Reputation: 225291
There’s not a much nicer way. The original Python should be using values
:
events = [e for foo in solution.values() for c in foo for e in c.events]
which you can reflect in JavaScript:
const events = [];
for (const foo of solution.values()) {
for (const c of foo) {
for (const e of c.events) {
events.push(e);
}
}
}
This is pretty short and easy to read (or it will be when foo
is replaced with an appropriate name). You can use concat
if you like creating lots of intermediate lists:
const events = [];
for (const foo of solution.values()) {
for (const c of foo) {
events = events.concat(c.events);
}
}
and reduce
if you like function calls that don’t really save space or readability, assuming the values of solution
are arrays:
const events = [];
for (const foo of solution.values()) {
events = foo.reduce(
(m, n) => m.concat(n.events),
events
);
}
and Array.from
and reduce
if you really like intermediate lists and don’t really like readability:
const events =
Array.from(solution.values()).reduce(
(events, foo) => events.concat(
foo.reduce(
(m, n) => m.concat(n.events),
events
)
),
[]
);
Defining more functions dulls the pain but doesn’t change the fact that ES6 is not so great
const concat = arrays =>
arrays.reduce((m, n) => m.concat(n), []);
const concatMap = (fn, arrays) =>
concat(arrays.map(fn));
const events = concatMap(
foo => concatMap(foo, c => c.events),
Array.from(solution.values())
);
Maybe the standard library is missing some iterator functions
function* map(fn, iterable) {
for (const x of iterable) {
yield fn(x);
}
}
function* concat(iterables) {
for (const iterable of iterables) {
yield* iterable;
}
}
const concatMap = (fn, iterables) =>
concat(map(fn, iterables));
const events = Array.from(
concatMap(
([key, foo]) => concatMap(c => c.events, foo),
solution
)
);
Stick with the for loops, honestly.
Upvotes: 1