1

I'm wondering if it's possible to achieve the below in a cleaner/more efficient way?

const data = [
    { active: false },
    { active: false },
    { active: true },
    { active: false },
    { active: true },
]

const split = (data) => {
    const activeData = data.filter(({active}) => active).map(element => 1)
    const inactiveData = data.filter(({active}) => !active).map(element => 0)

    return [
        activeData,
        inactiveData,
    ]
}

console.log(split(data))

The problem here, I think, is that this approach is not efficient, because it's filtering and mapping through the same array twice. Is there a way to do it just once?

Mike K
  • 7,621
  • 14
  • 60
  • 120
  • do you want only ones and zeros? please add the wanted result. – Nina Scholz Aug 04 '20 at 14:02
  • 1
    Define data just with array: `[false, false, true, false, true]` – Bhojendra Rauniyar Aug 04 '20 at 14:03
  • The result is not particularly important, it could be mapped to anything. I'm just asking if it's possible to avoid cycling through the same array twice, as in the example. The only requirement here, is that `data` is an array of objects. – Mike K Aug 04 '20 at 14:04
  • Cleaner will most likely be Opinion-based... – The_Black_Smurf Aug 04 '20 at 14:05
  • Use a `for..of` loop and push to 2 different arrays based on an `if` condition. – adiga Aug 04 '20 at 14:07
  • @adiga Yeah, that was only approach that I could think of, to avoid cycling twice through the array – Mike K Aug 04 '20 at 14:08
  • @MikeK [This](https://stackoverflow.com/questions/59050741/how-to-get-filtered-in-and-out-elements-of-array-at-one-go-in-javascript/) is related, it does the filtering in one iteration, but not the mapping, you could probably repurpose one of the solutions though to incorporate the mapping part. – Nick Parsons Aug 04 '20 at 14:12
  • 1
    instead of `.map()` you can use `.fill(1)` and `.fill(0)` – Get Off My Lawn Aug 04 '20 at 14:13

3 Answers3

1

Unless your array is immense, it's not going to make any difference. However you could indeed do this in one pass through the array:

    const split = (data) =>
      data.reduce((rv, entry) => 
        (rv[+!entry.active].push(+entry.active), rv)
      , [[], []]);
      
    console.log(split([{active: true}, {active: false}, {active: false}]))

The + operators convert the boolean values to numbers (0 or 1). The "rv" parameter to the .reduce() callback is the "return value", or the accumulator into which the .reduce() callback builds up the result. In this code, the callback uses the "active" flag (negated) to pick either the first array (index 0) or the second array (index 1) in the accumulator "array of arrays". The value pushed into the chosen array is then either 0 or 1, again obtained from the "active" value by converting it to a number.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • This an interesting approach and seems to be what I'm looking for. Can you please elaborate what's going on here? Does `rv` here refer to both of the initial arrays of the reducer? Also, it doesn't seem to work without the cast number (doing `(rv[+!entry.active].push(+entry.active), rv)` seems to crash it), does this mean it'll only work with numbers and not boolean values, for example? – Mike K Aug 04 '20 at 14:42
  • @MikeK I'll add a little explanation to the answer; it's basically what `.reduce()` is all about. – Pointy Aug 04 '20 at 14:54
0

You could take a single loop and reduce the array using an object as target.

const
    data = [{ active: false }, { active: false }, { active: true }, { active: false }, { active: true }],
    { true: activeData, false: inactiveData } = data.reduce((r, { active }) =>
        (r[active].push(+active), r),
        { true: [], false: [] }
    );

console.log(activeData);
console.log(inactiveData);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

You pretty much have a clean way to do it, however using multiple array functions requires more loops over the data.

Reduce will allow us to do this just once.

const data = [
    { active: false },
    { active: false },
    { active: true },
    { active: false },
    { active: true },
]

const split = (data) =>
  data.reduce((acc, {active}) => acc[+!active].push(+active) && acc, [[],[]])

console.log(split(data))
Get Off My Lawn
  • 34,175
  • 38
  • 176
  • 338