Live data from Hacker News

Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

wisdomgeek.com

61–70 of 97 posts

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#61

Earlier quoted context omitted.

You could do that, but I’d argue using filter then map is more readable. What do empty arrays have to do with doubling even integers?

The interesting generalization is that once you realize that flatMap lets you map and filter at the same time is that you can generate arbitrary elements in the output list corresponding to each item in the input list. For example, ls.flatMap(x => { if (x gives you all the real square roots from the original list, doing the mapping, flattening, and filtering all in one function call.

(just for laughs)

  ls.filter(o=>0o||[Math.sqrt(x, -Math.sqrt(x)])

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#62
post #31

Earlier quoted context omitted.

Small mistake, and maybe that's why this function exists, but it's actually combines `.map(fn).flat(1)`. I think conceptually, `flatMap` is a `map` where each iteration can return multiple values (or none). So it feels quite more powerful than just `map` and is a quite common convenience function.

I think the depth argument of flat defaults to 1, so it'd be the same anyway. Not sure why the default is 1, I'd prefer Infinity so it'd always flatten everything by default, but maybe there are some good reasons for it.

I think it should be explicit without a default.

But in the real world, if we must have a default, I think it should be 1, not infinity.

Infinity has a frustrating fail state, in my opinion: you might be flattening arrays that weren't meant to be flattened. And now you've got a cluster!@#$ of data to look at and reason about.

The fail state of 1 is that it doesn't flatten enough but you have the original structure in your 1-level flattened output. So you're far more likely to make heads and tails of how much more flattening you need.

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#63
post #46

Earlier quoted context omitted.

If you're just iterating through the array and mutating an object on each iteration, just use a for loop.

Obviously you can alternately write: let input = [1, 2, 3, 4, 5], output = []; for (let i = 0; i But in some circumstances the other style can be more convenient / legible. The immediate question was about pushing to an array and then returning the array, for which the comma operator can be handy.

No argument that the comma operator is a neat trick when you need it.

FWIW, it's 2022:

  const output = [];
  for (const n of [1, 2, 3, 4, 5]) {
    if (n % 2) output.push(2 * n);
  }

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#64

Something that people may not see immediately is that flatMap is more general than map and filter. Say, for a contrived example, that you'd like to filter out the even numbers in an array, and then double the odd numbers that remain. Instead of: [1, 2, 3, 4, 5].filter(n => n % 2 === 1).map(n => n * 2) You can do: [1, 2, 3, 4, 5].flatMap(n => n % 2 === 1 ? [n * 2] : []) Again, this is a contrived example, but I think…

You could do that, but I’d argue using filter then map is more readable. What do empty arrays have to do with doubling even integers?

>What do empty arrays have to do with doubling even integers

nothing, but they do have some relationship to 0, "" and Promise.resolve() - the array is handling the logic that will make the results be combined, not the doubling part

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#65
post #7

I've made it a habit to check Mozilla's JS docs once in a while for functions like Flat() and FlatMap(). Sometimes if I find myself reaching for underscore/lodash, I'll check Mozilla docs first to see if there's some new function that can let me omit using lodash. I'm often delighted to find new convenience functions I can just use without adding another dependable.

Is there a Google equivalent to MDN?

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#66

Something that people may not see immediately is that flatMap is more general than map and filter. Say, for a contrived example, that you'd like to filter out the even numbers in an array, and then double the odd numbers that remain. Instead of: [1, 2, 3, 4, 5].filter(n => n % 2 === 1).map(n => n * 2) You can do: [1, 2, 3, 4, 5].flatMap(n => n % 2 === 1 ? [n * 2] : []) Again, this is a contrived example, but I think…

You could do that, but I’d argue using filter then map is more readable. What do empty arrays have to do with doubling even integers?

Filter and then map will iterate the list twice. JS really needs some iterator-based methods like Lodash where it will only go through the list once in this case.

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#67
post #42

Earlier quoted context omitted.

Ah, sorry, so you have to concat the arrays using `concat`. [1, 2, 3, 4, 5].reduce((acc, n) => n % 2 === 1 ? acc.concat([2*n]) : acc, [])

Surely the spread operator is nicer here? [1, 2, 3, 4, 5].reduce((acc, n) => n % 2 === 1 ? [ ...acc, 2 * n ] : acc, [])

This is not efficient. Each iteration creates a new array instance due to the spread operator.

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#68

Something that people may not see immediately is that flatMap is more general than map and filter. Say, for a contrived example, that you'd like to filter out the even numbers in an array, and then double the odd numbers that remain. Instead of: [1, 2, 3, 4, 5].filter(n => n % 2 === 1).map(n => n * 2) You can do: [1, 2, 3, 4, 5].flatMap(n => n % 2 === 1 ? [n * 2] : []) Again, this is a contrived example, but I think…

If you are looking for really general and powerful, then there is the mighty reduce:

    [1, 2, 3, 4, 5].reduce((x, y) => y % 2 === 1 ? [...x, y * 2] : x, [])

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#69

Something that people may not see immediately is that flatMap is more general than map and filter. Say, for a contrived example, that you'd like to filter out the even numbers in an array, and then double the odd numbers that remain. Instead of: [1, 2, 3, 4, 5].filter(n => n % 2 === 1).map(n => n * 2) You can do: [1, 2, 3, 4, 5].flatMap(n => n % 2 === 1 ? [n * 2] : []) Again, this is a contrived example, but I think…

In addition to being essentially a combined "filter" and "map", it's also a "better" filter than filter itself in TypeScript in such that it narrows types much more ergonomically[0]. In TypeScript, you might have an array of multiple types (e.g. `Array `), and use a `filter` call to only keep the `A`s. However, in many situations TypeScript can't figure this out and the resulting array type is still `Array `. However…

I wonder if it is possible to add a feature to Typescript to help with this:

You could potentially add a syntax for type guards function types, then add a signature to filter that accepts a type guard and returns an array of the guarded types.

Shouldn't be too much of a stretch given that we have type guards.

The syntax is a bit annoying... should be something like filter(cb: A => A is B)

:/

Re: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

#70
post #31

Earlier quoted context omitted.

I think the depth argument of flat defaults to 1, so it'd be the same anyway. Not sure why the default is 1, I'd prefer Infinity so it'd always flatten everything by default, but maybe there are some good reasons for it.

I think it should be explicit without a default. But in the real world, if we must have a default, I think it should be 1, not infinity. Infinity has a frustrating fail state, in my opinion: you might be flattening arrays that weren't meant to be flattened. And now you've got a cluster!@#$ of data to look at and reason about. The fail state of 1 is that it doesn't flatten enough but you have the original structure in…

Arguably the same argument works the other way around as well, that in thinking flat recursively flattens all arrays of arrays you get confused when it doesn't. (I've definitely run across that footgun.)

For me, I just find that in almost all cases I've used flat it's been with Infinity as the depth value. It seems to me as well that most of the time you'd probably either want 1 or Infinity, not 5 or 12 or whatever, so perhaps it would've even been better to just have two functions: flat and flatDeep (or some such, naming is hard) the latter of which would default to Infinity but allow different depths as well.

Probably reads better than just flat as well, e.g. `.flatDeep(5)` rather than `.flat(5)`. Oh well, we're stuck with this now so it's all an academic exercise anyway, but I'd be curious to know the rationale of the design. Maybe I'll wade through the spec repo one day to see if there's any discussion about it.

Post reply on HN