1

I'm wondering, if there is a way to filter an array or stream and apply a function A to all matches and a function B to all non-matches in JavaScript. Here is some example code that explains it a bit more:

  // initial data
  var names = ['Matthias', 'Maria', 'Bob', 'Anton'];
  var namesWithM;
  var namesWithoutM;

  // gets only names starting with M, but not the others at the same time
  namesWithM = names.filter(name => name.startsWith('M'))

  // conditional lambda version
  namesWithM = [];
  namesWithoutM = [];
  names.forEach(name => name.startsWith('M') ? namesWithM.push(name) : namesWithoutM.push(name));


  // conditional classical version
  namesWithM = [];
  namesWithoutM = [];
  names.forEach(function(name) {
    if (name.startsWith('M'))
      namesWithM.push(name)
    else
      namesWithoutM.push(name);
  });

The very first version handles just the matches but uses filter and not forEach. Is there any way to use filter and apply a function for matches and non-matches at once? Something like this pseudo code:

names.filter(name.startsWith('M')).apply(namesWithM::push).or(namesWithoutM::push);
0

3 Answers 3

2

filter returns an array. So you can use this array to fill with name which either starts with M or not.

In the below example the filter is filling the array with name starts with M. In filter callback the name not starting with M are filled in another array

// initial data
var names = ['Matthias', 'Maria', 'Bob', 'Anton'];
var namesWithM;
var namesWithoutM = [];

namesWithM = names.filter((name) => {
  if (!name.startsWith('M')) {
    namesWithoutM.push(name)
  }
  return name.startsWith('M');
});

console.log(namesWithM, namesWithoutM);

Sign up to request clarification or add additional context in comments.

Comments

1

I would use reduce to group data into 2 mentioned cases. I don't see any reason to use filter here

let names = ['Matthias', 'Maria', 'Bob', 'Anton'];

let [namesWithM, namesWithoutM] = names.reduce((acc, name) => {

  if (name.startsWith('M')) {
    acc[0] = [...(acc[0] || []), name]
    return acc;
  }

  acc[1] = [...(acc[1] || []), name]
  return acc;

}, [])

// simpler version
console.log(namesWithM, namesWithoutM);

let [namesWithM1, namesWithoutM1] = names.reduce((acc, name) => {
    const index = Number(!name.startsWith('M'));
    acc[index] = [...(acc[index] || []), name];
    return acc;
}, [])

console.log(namesWithM1, namesWithoutM1);

3 Comments

True, but that's quite long code again. But nice example of reduce and accumulators. I would use that in my version where I used the conditional lambda code but use reduce instead of filter there.
Updated answer. I can't compress it further
Found this nice explanation on how to Make two calculations in one traversal which is more or less the same idea as my question.
0
const names = ['Matthias', 'Maria', 'Bob', 'Anton'];
function A(item){
  console.log('filtered');
  console.log(item);
}
function B(item){
  console.log('not-ffiltered');
  console.log(item);
}
const filteredNames = names.filter(name => {
  const isValid = name.startsWith('M')
  if(isValid)
    A(name)
  else
    B(name)
  return isValid;
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.