Problem
Using the array.filter function, I can efficiently pull out all elements that do or do not meet a condition:
let large = [12, 5, 8, 130, 44].filter((x) => x > 10);
let small = [12, 5, 8, 130, 44].filter((x) => !(x > 10));
However, in the example above, I’m iterating over the array twice and performing the same test each time. Is there a simple way to generate both ‘large’ and ‘small’ in a single pass over the array? In particular, if the callback to evaluate whether an element should be kept is expensive, I’d like to avoid calling it twice.
Solution
Using TypeScript/ECMAScript 6 syntax it can be achieved this way. I am not sure whether it’s more or less elegant compared to the original variant, but
- It does the job;
- Requires only one run;
- Can be further chained with
map ()
or other functions.
const [small, large] = // Use "deconstruction" style assignment
[12, 5, 8, 130, 44]
.reduce((result, element) => {
result[element <= 10 ? 0 : 1].push(element); // Determine and push to small/large arr
return result;
},
[[], []]); // Default small/large arrays are empty
More options can be found in various StackOverflow questions.
Anything wrong with the naive ways?
let large = [];
let small = [];
array.forEach((x) => (x > 10 ? large : small).push(x));
// or
for(const x of array){
(x > 10 ? large : small).push(x);
}
The existing answers do not seem to address the callback. One way to include it could be:
const partition = (ary, callback) =>
ary.reduce((acc, e) => {
acc[callback(e) ? 0 : 1].push(e)
return acc
}, [[], []])
and using it like:
let [large, small] = partition([12, 5, 8, 130, 44], (x => x > 10))