Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get filtered in and out elements of array at one go in Javascript

I wonder if there is a precise way for getting filtered an unfiltered elements of an array in Javascript, I mean, like, in one go.

Currently, I use a logic like follows:

const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = ['a', 'b']

// I want to combine those two expressions somehow
const filteredInResult = myArray.filter(e => filterArray.includes(e))
const filteredOutResult = myArray.filter(e => !filterArray.includes(e))

console.log(filteredInResult)
console.log(filteredOutResult)

I felt like a destructuring-like way might already be there to achieve it, but anyway, I prefer asking you guys if there is a way for getting filtered in & out results in one shot.

EDIT: SO keeps alerting me if this question is similar to the question here, but I used string comparison and includes for brewity above but the filtering expression may be more complex than that. So, the I must underline that the focus of the question is not on difference of two string arrays. I am leaving another example and hope the questions won't be merged :D

// A more complex use case
const myArray = [
  {id: 1, value: 'a'},
  {id: 2, value: 'b'},
  {id: 3, value: 'c'},
  {id: 4, value: 'd'},
  {id: 5, value: 'e'},
]
const filterArray = ['a', 'b']

// I want to combine those two expressions somehow
const filteredInResult = myArray.filter(e => filterArray.includes(e.value))
const filteredOutResult = myArray.filter(e => !filterArray.includes(e.value))

console.log(filteredInResult)
console.log(filteredOutResult)
like image 667
vahdet Avatar asked Nov 26 '19 12:11

vahdet


2 Answers

If you're worried about iterating twice over the myArray, you might first consider reducing the computational complexity. Because each iteration of the loops calls Array.prototype.includes, and the complexity of Array.prototype.includes is O(n), your code has an overall complexity of O(n ^ 2). (outer loop: O(n) * inner loop: O(n)). So, consider fixing that first: use a Set and Set.has, an O(1) operation, instead of an array and .includes. This is assuming that your actual filterArray is large enough that computational complexity is something to worry about - sets do have a bit of an overhead cost.

As for the other (main) part of the question, one option is to create the two result arrays outside, then push to the appropriate one while iterating:

const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = new Set(['a', 'b'])

const filteredInResult = [];
const filteredOutResult = [];
for (const e of myArray) {
  (filterArray.has(e) ? filteredInResult : filteredOutResult).push(e);
}
console.log(filteredInResult)
console.log(filteredOutResult)

Could also use reduce, though I don't think it looks very good:

const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = new Set(['a', 'b'])

const { filteredInResult, filteredOutResult } = myArray.reduce((a, e) => {
  a[filterArray.has(e) ? 'filteredInResult' : 'filteredOutResult'].push(e);
  return a;
}, { filteredInResult: [], filteredOutResult: [] });

console.log(filteredInResult)
console.log(filteredOutResult)
like image 71
CertainPerformance Avatar answered Oct 21 '22 05:10

CertainPerformance


You could use .reduce() instead of .filter(), where you use the (numeric) boolean value of includes() as the index for your accumilator like so:

const myArray = ['a', 'b', 'c', 'd', 'e'];
const filterArray = ['a', 'b'];

const [fOut, fIn] = myArray.reduce((a, n) => {
  a[+filterArray.includes(n)].push(n); 
  return a;
}, [[], []]);

console.log(fIn);
console.log(fOut);
like image 5
Nick Parsons Avatar answered Oct 21 '22 04:10

Nick Parsons