Array.prototype.partition - Like `filter`, but two arrays (for `true` and `false`)

Hi!

I'd like to propose a new Array.prototype.partition function, following the prior art of other languages (that can be seen in the proposal repository, alongside the draft spec and polyfill).

partition should be an utility function that returns two arrays based on the items' conformity to a predicate. Similarly to filter, that returns an array to those items that satisfy the predicate, partition would return two arrays - one with those items that satisfy the predicate, and one with those that don't.

const numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
const isEven = (n) => n % 2 === 0;

const [even, odd] = numbers.partition(n => isEven(n));

Currently the options I see for this are either:

  1. doing a .filter twice, which is tough on performance when working with larger datasets
  2. doing a for loop with conditional push
  3. doing a .reduce with concats on the accumulator items.

One of them reads well but is not ideal, since you have to loop over twice. The other two are harder to read and reason about.

This function is already present in several languages and the usage seems intuitive enough (not attached on the name, btw, wouldn't mind changing it to something like split or others).

This function has been present in most of the utility libraries that people, and I'm confident offering it (like includes) in the Array prototype itself would be a nice addition.

9 Likes

I'd like to see this as well :-)

Another language you can add as prior art in the proposal is Haskell's partition.

2 Likes

People will always have to think about in which order the result will be. In the example above, it is [true, false]. But I wonder if it would be better to let the order be reversed so that the "false" results are at array index 0, and the "true" results are at array index 1. At least I would remember that well... :)

1 Like

That's true! I wouldn't mind changing it too. Your point is really pertinent, thanks for pointing out!

That could be easily addressed by making the return value an object. (Also, returning an object makes it slightly easier for engines to optimize for.)

1 Like

On what do you base that optimization claim?

I said "slightly" in that they don't also need to verify that Array.prototype[Symbol.iterator] isn't modified. Not that it would significantly affect the performance of already optimized code, just that it'd ever so slightly simplify an optimization pass.

Just throwing another idea. How about a mutating reject method instead of (or maybe even in addition to) the proposed partition? I'm aware of the implications which come from doing both tasks at once, the filtering reject and especially the array mutating remove part which would force reindexing all array items to the right of where removing one item took place. Being pretty confident about how the latter can be taken care of performance-wise, especially for huge arrays, how was the acceptance or the need then for a native reject?

Btw I already suggested this in proposal-array-filtering proposal:

Another idea is make it more general and add slide, stride and chunk features: