Destructuring in combination with array functions

Viewed 297

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!

3 Answers

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)}`)

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 },
//   ],
// } 

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);

Related