25
\$\begingroup\$

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.

\$\endgroup\$
21
\$\begingroup\$

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

  1. It does the job;
  2. Requires only one run;
  3. 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.

| improve this answer | |
\$\endgroup\$
13
\$\begingroup\$

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);
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Why map instead of forEach? \$\endgroup\$ – xehpuk Feb 15 '18 at 20:22
  • \$\begingroup\$ @xehpuk you are right, forEach would be better here. Updated the answer. \$\endgroup\$ – Qtax Feb 19 '18 at 10:27
3
\$\begingroup\$

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))
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ While I think calling a predicate in partition function a "callback" is a bit vague, it's actually a decent solution that you posted. 👍 \$\endgroup\$ – rishat Feb 19 '18 at 15:13
  • 2
    \$\begingroup\$ @RishatMuhametshin nomenclature was from OP \$\endgroup\$ – morbusg Feb 19 '18 at 15:16
  • \$\begingroup\$ Rereading it, I'm a bit surprised that I used the term "callback." In fact, I think the code that prompted me to ask this question involved an array of promises, so "callback" was a particularly inopportune way to describe the predicate. \$\endgroup\$ – Max Rosett Feb 19 '18 at 19:12
  • \$\begingroup\$ Usually you'd go with predicate as the name. \$\endgroup\$ – Christian Ivicevic Aug 23 '19 at 14:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.