简体   繁体   中英

Nested for-loop in idiomatic JavaScript

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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM