Skip to content

Instantly share code, notes, and snippets.

@coolaj86
Last active February 23, 2022 04:42
Show Gist options
  • Save coolaj86/4af3a41b14229f1b24c16c8f95fbe181 to your computer and use it in GitHub Desktop.
Save coolaj86/4af3a41b14229f1b24c16c8f95fbe181 to your computer and use it in GitHub Desktop.

(Ab)uses of Reduce

Watch: https://youtu.be/X85QdHe1tDw?t=409 (Starts at 6:50)

reduce is one of the least understood Array methods, and probably the most abused.

Use #0 Actually Reducing

Reducing, as the good functional programmer intended:

let total = arr.reduce(function (acc, el) {
  return acc + el;
}, 0);

Subtle difference (read: bug) in JavaScript:

let total = arr.reduce(function (acc, el) {
  return acc + el;
});

(when the initial value is omitted, the first array element is used)

(Acceptable) Abuse #1: Chaining promises

await arr.reduce(async function (promise, fn) {
  await promise;
  await fn();
}, Promise.resolve())

Which is the same as this:

let p = Promise.resolve();
arr.forEach(function (fn) {
  p = p.then(function () {
    return fn();
  });
});
await p;

Which is the same as this:

Promise.resolve()
  .then(function () {
    var fn = arr[0];
    return fn();
  })
  .then(function () {
    var fn = arr[1];
    return fn();
  })
  .then(function () {
    var fn = arr[2];
    return fn();
  })
  .then(function () {
    var fn = arr[...];
    return fn();
  })

vs Promise.all()

Promise.all() will run in parallel (all-at-once), not in sequence (one-after-the-other).

Questionable Abuse #2: Inverse .map

reduce can be a sort of inverse of map.

You can use map to create arrays from arrays or objects:

arr.map(function (el) {
  // ...
})
let arr = Object.keys(hashmap).map(function (k) {
  let val = hashmap[k];
  val.name = k;
  return val;
});

You can use reduce to create objects from objects or arrays:

let obj = arr.reduce(function (obj, el) {
  obj[el.name] = el;
  return obj;
}, {});

However, forEach is easier to read and reason about:

let obj = {};
arr.forEach(function (el) {
  obj[el.name] = el;
});
@coolaj86
Copy link
Author

async function sleep(n) {
    await new Promise(function (resolve) {
        setTimeout(resolve, n);
    });
}

await Promise.all([1000, 30, 500, 3000].map(async function (n, i) {
  await sleep(n);
  console.log('Done', i);
}));
async function sleep(n) {
    await new Promise(function (resolve) {
        setTimeout(resolve, n);
    });
}

await [1000, 30, 500, 3000].reduce(async function (promise, n, i) {
  await promise;
  await sleep(n);
  console.log('Done', i);
}, Promise.resolve());

@coolaj86
Copy link
Author

let Fs = require('fs').promises;

let err = await Fs.access('./path/to/thing.ext').catch(Object);
if (err) {
  // mkdir
}

@coolaj86
Copy link
Author

Focus on for. If you can do it all with for, you won't struggle with the syntax sugars.

for (;;) {
  // ...
}
while (true) {
  // ...
}
do {
} while ()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment