68

I realize that I can do:

arr = arr.filter(function(n){ return !filterFunc(n); });

But is there any way to just invert a filter without wrapping the filterer in an anon function?

It just seems cumbersome.

4
  • 3
    To my knowledge, no. But you can make one: Array.prototype.unfilter = function(cb) {return this.filter(function(a,b,c) {return !cb(a,b,c);});}; Commented Nov 22, 2013 at 15:20
  • 9
    I'd better go more functional way function not(func) { return function(){return !func.apply(this, arguments);};}. Usage arr = arr.filter(not(criterion));. Commented Nov 22, 2013 at 15:42
  • You're missing the point with your code. You should modify filterFunc to return the opposite of what it does, not return a function call within a function call. It's only cumbersome because you made it so. As a result, this entire question is more than a little confusing at first glance. It wasn't until I had looked at the third answer that I realized what had happened.
    – Kyle Baker
    Commented Nov 1, 2016 at 20:00
  • 2
    @kyle baker Maybe you're confused by the OP's example code, but it seems like you're arguing that it's wrong for them to write a single filtering function once, and then use it to get both the set that matches that filter and the set that doesn't. Just because Javascript didn't implement an inverse of filter at the language level doesn't mean everyone should have to write two filtering functions just to separate an array in to two groups. And as the answers below show, OP can get exactly what they are asking for with Lodash or an arrow function without having to rewrite filterFunc. Commented Apr 3, 2017 at 17:40

9 Answers 9

54

You can use an arrow function:

const a = someArr.filter(someFilter);
const a = someArr.filter(e => !someFilter(e));
2
  • 1
    @KyleBaker no you shouldn't. The concept of negation can be encoded in single function and composed with other functions we wish to invert. Do you wonder why we were given Array.isArray but notArray.isNotArray? Some things have natural inverses but describe different qualities of elements in their domain – eg,isOdd is the natural inverse of isEven but both are useful because they express what is about a particular element. It would be silly to have isEven and isNotEven or isOdd and isNotOdd.
    – Mulan
    Commented Apr 3, 2017 at 18:21
  • 1
    If you want to negate a function, donot make a copy of it and invert the output. Instead represent negation once (eg) not = x => !x and compose it with f
    – Mulan
    Commented Apr 3, 2017 at 18:25
37

Lodash provides a reject function that does the exact opposite of filter.

arr = _.reject(arr, filterFunc);
1
  • 3
    i'm here because i forgot how the inverse of _.filter is called. Thank you! Commented May 13, 2020 at 5:49
8

Take a look at lodash's negate function. It does exactly what @Yury Tarabanko mentions in his comment.

Usage:

arr = arr.filter(_.negate(filterFunc));
1
  • 4
    With lodash, you better use _.reject: The opposite of _.filter; this method returns the elements of collection that predicate does not return truthy for. lodash.com/docs/#reject
    – shudder
    Commented Feb 20, 2018 at 14:04
5

I wasn't happy with any of the answers directly, and actually wound up using newer JS features

arr.filter(() => ! filterfunc(...arguments));

This beats most of the others by not having to respecify the context (this) at any point by using an arrow function and passing all parameters accordingly using the spread syntax on the arguments object.

It's also rather succinct, though I would rather an invert flag on the filter function, or a separate function.

The question might be a little on the old side, but it's still relevant.

2
  • This is interesting, but doesn't work for normal functions (without rest parameters). It fails with A spread argument must either have a tuple type or be passed to a rest parameter.
    – MEMark
    Commented Dec 4, 2021 at 8:22
  • @MEMark Sounds like your arguments aren't being collected properly. Try: arr.filter((...args) => ! filterfunc(...args)); instead
    – SEoF
    Commented Dec 6, 2021 at 9:26
2

You can either add your own function or add a static/prototype methods to the Array object.

Code

Array Polyfill Methods

/**
 * The not() method creates a new array with all elements that fail
 * the test implemented by the provided function.
 *
 * Syntax
 * arr.not(callback[, thisArg])
 *
 * @param  callback
 *         Function to test each element of the array. Invoked with
 *         arguments (element, index, array). Return true to keep
 *         the element, false otherwise.
 * @param  thisArg
 *         Optional. Value to use as this when executing callback.
 * @return Returns a new array containing all the items which fail
 *         the test.
 */
Array.prototype.not = function(callback) {
    return this.filter(function () {
        return !callback.apply(this, arguments);
    });
};
/**
 * Static method which calls Array.prototype.not on the array
 * paramater.
 *
 * @see Array.prototype.not
 */
Array.not = function (array, callback) {
    return array != null ? array.not(callback) : [];
};

Custom Function

function unfilter(array, callback) {
    return array.filter(function () {
        return !callback.apply(this, arguments);
    });
}

This is safer to use than a polyfill, but it doesn't look as elegant in use.

unfilter(items, isFruit) vs items.not(isFruit)

Example

// ================================================================
// Polyfill
// ================================================================
Array.prototype.not = function(callback) {
    return this.filter(function () {
        return !callback.apply(this, arguments);
    });
};

// ================================================================
// Main
// ================================================================
var items = [{
    name: 'Apple',
    isFruit: true
}, {
    name: 'Carrot',
    isFruit: false
}, {
    name: 'Melon',
    isFruit: true
}, {
    name: 'Potato',
    isFruit: false
}];

var isFruit = function(item, index) {
    return item != null && item.isFruit;
};
var getName = function(item, index) {
    return item != null ? item.name : '?';
};

document.body.innerHTML = items.not(isFruit).map(getName).join(', ');

4
  • bad practice, and super heavy handed; the built in Array.prototype.filter() function is designed to handle its own inverse natively--that's why it accepts a function as an argument.
    – Kyle Baker
    Commented Nov 1, 2016 at 20:02
  • 2
    @KyleBaker, "designed to handle its own inverse natively" – do you care to explain how you're using the words "design" and "native"? writing a static inverse of the filtering function is wasteful when you can easily just use composition. eg, xs.filter(comp(not)(f)) where not = x =>!x and comp = f => g => x => f(g(x)) – I don't see how MrPolywhirl's method here is "heavy handed" especially considering it's implemented using Array.prototype.filter itself
    – Mulan
    Commented Apr 3, 2017 at 18:09
  • most importantly tho, it's crucial we allow ourselves to think of array filtering in both expressions: keep x's or remove y's – the point of abstracting this bevaiour in a reusable function is so that I don't have to juggle the cognitive load of inverse booleans or copies of filtering functions which are inverses of one another. Array.prototype.filter and an inverse of itself (whatever name you want), makes for a more versatile too because it gives us both expression forms without need for workarounds.
    – Mulan
    Commented Apr 3, 2017 at 18:14
  • A more specialised not function might be more expressive: If not = f => x => !f(x) then xs.filter(not(f)) effectively negates the supplied filter function.
    – gary
    Commented May 17, 2022 at 16:35
1

filter returns elements which return true in your evaluation. If you want to inverse that, inverse your logic it within the function which tests each element.

Then, you could simply make this function work like so:

arr = arr.filter(filterFunc);

0

There is some ways to do that:

example:

const randomNumbers = [10, 22, 36, 52, 58];

let NewArray = [];

1. Store it in new array:

NewArray = randomNumbers.filter((n)=> ! (n> 22))

2. Using lodash as @Sascha Klatt mentioned above:

NewArray = _.reject(randomNumbers , ((n)=> ! (n> 22)));

3. Make a function like this:

function rejected(params) {
  randomNumbers.forEach((val) => {
    if (!params(val)) NewArray.push(val);
  });
  return NewArray;
}

4. or this function (Almost same as above)

function rejected2(params) {
  randomNumbers.forEach((val) => {
    if (params(val)) {
    } else NewArray.push(val);
  });
  return NewArray;
}
0

If you really want to do this. You would have to write a helper function that returns a function that returns the inverted value of the normal output.

How you access this function is up to you. But I put in some examples.

Also I'm not saying it is better than writing a custom filter function for each scenario where you need it. You could even use the function you have ( you may or may not want this dependency ).

// APPROACH 1: CREATE A GENERIC HELPER FUNCTION
const arr = [1,2,"a","b"];
function is_a_string( val ) {
  return typeof val === 'string';
}

function not( callback ) {
  return ( ...arg ) => !callback( ...arg );
}

console.log( arr.filter( not( is_a_string ) ) );

// APPROACH 2: EXTEND A SPECIFIC FUNCTION ( hoisting issue )
const arr = [1,2,"a","b"];
function is_a_string( val ) {
  return typeof val === 'string';
}
// note that hoisting is a thing here
is_a_string.not = ( ...args ) => !is_a_string(...args );
console.log( arr.filter( is_a_string.not ) );

// APPROACH 3: CREATE ANOTHER FUNCTION
const arr = [1,2,"a","b"];
function is_a_string( val ) {
  return typeof val === 'string';
}

function is_not_a_string( val ) {
  return !is_a_string( val );
  // or just `return typeof val !== 'string'`; in a simple case like this
}
console.log( arr.filter( is_not_a_string ) );

-1

Lets take an example

var cars = [{
    carname: "indica",
    brand: "Tata"
  },
  {
    carname: "accord",
    brand: "Toyota"
  },
  {
    carname: "vento",
    brand: "volkswagen"
  },
  {
    carname: "polo",
    brand: "volkswagen"
  },
  {
    carname: "Manza",
    brand: "Tata"
  },
  {
    carname: "Agile",
    brand: "Chevrolet"
  },
];

var isTata = function(car) {
  return car.brand === "Tata"
}

var fiteredCars = cars.filter(isTata); // retuns objects of brand Tata

console.log(fiteredCars)

in reverse of this just change your logic

var isNotTata = function(car) {
  return car.brand !== "Tata"
}
var dogs = cars.filter(isNotTata); // returns objects of brand other than Tata
1
  • 2
    This solution requires you to redefine your filter with the inverse predicate, effectively creating a new filter. OP asked for a simple inversion mechanism of an existing filter.
    – Jesper
    Commented Jan 19, 2021 at 14:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.