2

Just wondering if it's possible to use an array function (like filter) to return both the negative and the positive outcomes of the statement at the same time using destructuring.

Something like the following:

let {truthys, falsys} = arr.filter(a => {
   return //magical statement that returns truthy's and falsy's?
}); 

instead of:

let truthys = arr.filter(item => item.isTruthy);
let falsys = arr.filter(item => !item.isTruthy);

So something of a shorthand-way of doing the latter. Can't seem to find anything about this anywhere so it might not be possible at all. Thanks!

Simon Rook
  • 185
  • 1
  • 3
  • 13
  • 2
    You could use `.reduce()` to create an object with two arrays. – Pointy Jun 11 '21 at 15:04
  • `.filter()` returns *one* array of values. Doesn't make much sense to destructure it, if you pass a predicate. What you can do is *group* by the predicate result and return an object with `{ "true" : /* items that passed the predicate test */, "false": /* items that did not pass the predicate test */ }` and then destructure as `const {true: truthys, false: falsys} = groupedValues` – VLAZ Jun 11 '21 at 15:07
  • Thanks! Will give both a try :) – Simon Rook Jun 11 '21 at 15:11
  • 1
    See [georg's `partition` implementation here](https://stackoverflow.com/a/67088441) it's essentially what you need. You'd call it like `parition(arr, x => !!x)` (or `parition(arr, Boolean)` if you wish). That's partitioning into an array with indexes `1` and `0` but the approach is the same if you want to partition into an object with keys `true` and `false`. I personally prefer the latter because it's a bit clearer that `result.true` is the result of everything where the condition returned `true` but ultimately doesn't matter much. – VLAZ Jun 11 '21 at 15:22
  • 1
    `const [thruthys, falsys] = partition(arr, item => item.isTruthy)` with an appropriate helper function (see the duplicates) is the standard approach. If you already have a `groupBy` helper, such as lodash's one, you can also use `const {true: truthys, false: falsys} = _.groupBy(arr, item => !!item.isTruthy)`. – Bergi Jun 11 '21 at 16:11

3 Answers3

0

Your idea can never work as written because the return from filter is necessarily an array, not a structure. If you don't mind just finding one value, this variation works:

{ a, b } = [ { a: 'yyy', b: 'yzz' }, { a: 'aww', b: 'azz' } ].find(e => e.a.startsWith('y'))

> a
'yyy'
> b
'yzz'

But looking more closely I see What You Actually Wanted, so perhaps the most straightforward is:

const a = [ '', ' hello ', ' ', false, [], [0], [''], [' '], null, undefined, new Array(), 0, 1, -1 ]
const { truthies, falsies } = { truthies: a.filter(e => !!e), falsies: a.filter(e => !e) }
console.log(`truthies: ${JSON.stringify(truthies)}\nfalsies: ${JSON.stringify(falsies)}`)

With the result:

{
  truthies: [ ' hello ', ' ', [], [ 0 ], [ '' ], [ ' ' ], [], 1, -1 ],
  falsies: [ '', false, null, undefined, 0 ]
}

EDIT

Inspired by keeping just one filter pass, it's not so pretty, but one can do:

const a = [ '', ' hello ', ' ', false, [], [0], [''], [' '], null, undefined, new Array(), 0, 1, -1 ]
const false_temp = []
const { truthies, falsies } = { truthies: a.filter(e => !!e || (false_temp.push(e) && false)), falsies: false_temp }
console.log(`truthies: ${JSON.stringify(truthies)}\nfalsies: ${JSON.stringify(falsies)}`)
BaseZen
  • 8,650
  • 3
  • 35
  • 47
-1

You can use .reduce for it:

const getTruthysAndFalsys = (array) => {
  return array.reduce(
    ({ truthys, falsys }, item) => {
      const isTruthy = item.isTruthy

      return {
        truthys: [
          ...truthys,
          ...(isTruthy ? [item] : []),
        ],
        falsys: [
          ...falsys,
          ...(!isTruthy ? [item] : []),
        ],
      }
    },
    { truthys: [], falsys: [] }
  )
}

const array = [
  { name: 'Item 1', isTruthy: true },
  { name: 'Item 2', isTruthy: true },
  { name: 'Item 3', isTruthy: false },
  { name: 'Item 4', isTruthy: true },
  { name: 'Item 4', isTruthy: false },
]


getTruthysAndFalsys(array)
// { 
//   truthys: [ 
//     { name: 'Item 1', isTruthy: true }, 
//     { name: 'Item 2', isTruthy: true }, 
//     { name: 'Item 4', isTruthy: true }, 
//   ], 
//   falsys: [ 
//     { name: 'Item 3', isTruthy: false }, 
//     { name: 'Item 4', isTruthy: false },
//   ],
// } 
Nolleto
  • 34
  • 2
-1

As @Pointy suggested you can avoid filtering two times by dividing the elements into two arrays with Array.prototype.reduce() like this:

const input = [1, 0, true, false, "", "foo"];

const [truthies, falsies] = input.reduce(
  ([truthies, falsies], cur) =>
    !cur ? [truthies, [...falsies, cur]] : [[...truthies, cur], falsies],
  [[], []]
);

console.log(truthies, falsies);
Guerric P
  • 30,447
  • 6
  • 48
  • 86