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.
You can use an arrow function:
const a = someArr.filter(someFilter);
const a = someArr.filter(e => !someFilter(e));
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
.
not = x => !x
and compose it with f
Lodash provides a reject function that does the exact opposite of filter.
arr = _.reject(arr, filterFunc);
Take a look at lodash's negate function. It does exactly what @Yury Tarabanko mentions in his comment.
Usage:
arr = arr.filter(_.negate(filterFunc));
_.reject
: The opposite of _.filter; this method returns the elements of collection that predicate does not return truthy for. lodash.com/docs/#reject
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.
arr.filter((...args) => ! filterfunc(...args));
instead
You can either add your own function or add a static/prototype methods to the Array object.
/**
* 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) : [];
};
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)
// ================================================================
// 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(', ');
Array.prototype.filter()
function is designed to handle its own inverse natively--that's why it accepts a function as an argument.
Commented
Nov 1, 2016 at 20:02
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
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.
not = f => x => !f(x)
then xs.filter(not(f))
effectively negates the supplied filter function.
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);
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;
}
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 ) );
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
Array.prototype.unfilter = function(cb) {return this.filter(function(a,b,c) {return !cb(a,b,c);});};
function not(func) { return function(){return !func.apply(this, arguments);};}
. Usagearr = arr.filter(not(criterion));
.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 rewritefilterFunc
.