Javascript: place elements that do not match the filter predicate in a separate array

It might be a lot easier than I think, but I tried the .map () and .filter () functions in javascript. I want to create one array using .filter () and another array for elements that did not match the predicate for the first filter. What I still have:

function test(array, predicate){ var filterTrue = array.filter(predicate); var filterFalse = ?? // rest of method } 

Is there a way to discard elements that do not match the predicate in filterFalse? Perhaps it goes without saying, but a predicate will usually be a function of some

EDIT: Aside, I tried:

 var filterFalse = array.filter(!predicate); 

But this doesn't seem to work for reasons I'm still trying to understand (any help on this would also be greatly appreciated)

+9
source share
4 answers

In this case, you're better off with forEach , but I am addressing your question about why !predicate didn't work (and how you can do something like that) below.

First, a simple forEach solution:

Prosiac:

 function test(array, predicate){ var filterTrue = []; var filterFalse = []; array.forEach(function(value) { if (predicate(value)) { filterTrue.push(value); } else { filterFalse.push(value); } }); // rest of method } 

A bit more concise:

 function test(array, predicate){ var filterTrue = []; var filterFalse = []; array.forEach(function(value) { (predicate(value) ? filterTrue : filterFalse).push(value); }); // rest of method } 

Aside, I tried:

 var filterFalse = array.filter(!predicate); 

But this does not seem to work for reasons that I am still trying to understand.

It should be:

 var filterFalse = array.filter(function(entry) { return !predicate(entry); }); 

... and it really will work, but that means you make two passes through the array and call the predicate twice for each element. Therefore, I recommended forEach : only one pass through the array is required and only one predicate call for each record.

The reason your var filterFalse = array.filter(!predicate); didn't work, is that it takes a predicate variable that contains a reference to the function and logically inverts it with ! . The logically inverted version of the reference to a nonzero object (functions are objects) is false , so you actually passed false to filter .

More fully: unary ! forces its operand to have a boolean value and then returns the opposite ( false for true and true for false ). Thus !predicate will result in false for any predicate value that will depend on true (in other words, "true" values) and will lead to true for any predicate value that will depend on false (otherwise called "false" values). So what are the “true” and “false” values? False, 0 , null , undefined , NaN and, of course, false ; The "right" values ​​are all others, including all references to null objects.

If you program a lot with predicates and want to say “not a predicate” and get a function that gives you an inverted result, you can do it like this:

 function not(predicate) { return function() { return !predicate.apply(this, arguments); }; } 

and then:

 var filterFalse = array.filter(not(predicate)); 

This function not works as follows: it returns a new function that, when called, will call the predicate function that you gave it, passing by the value this with which it was called, and all the arguments that it called with (via Function#apply - spec | MDN ), logically inverting the return value obtained from the predicate, and then returning this inverted value.

But again, using this, it takes two passes through the array. Sometimes with high-level abstractions, however, this may be preferable to the forEach solution.

Finally, if you do a lot of this “or /” a lot, you can, of course, make a function for this:

 function divvyUp(array, predicate) { var filterTrue = [], filterFalse = []; array.forEach(function(value) { (predicate(value) ? filterTrue : filterFalse).push(value); }); return { filterTrue: filterTrue, filterFalse: filterFalse }; } 
+7
source

If you want rejected values, then take the filter in a function that returns the inverse predicate:

 function test(array, predicate) { var filterTrue = array.filter(predicate); var filterFalse = array.filter(not(predicate)); log(filterTrue, filterFalse); } function not(fn) { return function () { return !fn.apply(this, arguments); } } function log(filterTrue, filterFalse) { document.write('<pre> predicate true:' + JSON.stringify(filterTrue) + '</pre>'); document.write('<pre> predicate false: ' + JSON.stringify(filterFalse) + '</pre>'); } var array = [1, 2, 3, 4, 5]; function isEven(value) { return value % 2 === 0; } test(array, isEven); 
+3
source

EDIT :

The following is an implementation of the splitting method from lodash in plain JavaScript with TypeScript typing in JSDoc. It uses Array.prototype.reduce . As stated in the JSDoc comment, this sectioning function performs the following actions:

Returns an array with two arrays at index 0 and 1. An array with index 0 is all the elements in arr that passed the truth test predicate returning the true value. An array with index 1 is all the elements in arr that failed the predicate truth test, returning a false value.

 // ----- partition function declaration ----- /** Returns an array with two arrays at index * 0 and 1. The array at index 0 is all the items * in 'arr' that passed the 'predicate' truth test by * returning a truthy value. The array at index 1 is all the items * in 'arr' that failed the 'predicate' truth test by returning * a falsy value. * @template {any} T * @param {Array<T>} arr * @param {(el:T, index:number, arr:Array<T>) => any} predicate * @returns {[Array<T>, Array<T>]} */ function partition(arr, predicate) { return arr.reduce( function(acc, el, i, arr) { if (predicate(el, i, arr)) { acc[0].push(el); } else { acc[1].push(el); } return acc; }, [[], []] ); } // ----- function usage examples ----- // This partition gets all numbers which are even in the // first array (all these numbers returned true for the predicate) // and returns all numbers which are odd in the second array var res = partition([1, 2, 3], function(number) { return number % 2 === 0; }); console.log(res); // → [[2], [1, 3]] // This partition gets all indexes that are more than half // way through the array. res = partition([1, 2, 3, 4], function(number, index, array) { return index > Math.floor(array.length / 2) - 1; }); console.log(res); // → [[3, 4], [1, 2]] // This partition gets all strings with length greater than 4 res = partition(["bam!", "kazaam!", "blam!", "wam!", "jam!"], function(string, index, array) { return string.length > 4; }); console.log(res); // → [["kazaam!", "blam!"], ["bam!", "wam!", "jam!"]] 

The advantage of using JSDoc types is that if you have an editor such as VSCode, it will show you the typing and description when you press the command on mac or ctrl on the windows. It looks like this:

partition function VSCode typings output

VSCode is smart enough to understand, given that I used the T pattern in my JSDoc annotation, that T is a number because the array in parameter 1 in this figure is full of numbers. If you pass an array of strings, it will correctly prompt the types of the predicate parameter el and the values ​​of the returned element of the internal arrays as strings, since they also use the T.


Original answer

I hate raising libraries if you are not using them yet, but lodash has a function that does this, called a section .

 _.partition([1, 2, 3], function(n) { return n % 2; }); // → [[1, 3], [2]] _.partition([1.2, 2.3, 3.4], function(n) { return this.floor(n) % 2; }, Math); // → [[1.2, 3.4], [2.3]] 

Creates an array of elements, divided into two groups, the first of which contains elements, the predicate returns true for, while the second of which contains the predicate of elements returns false for. The predicate is bound to thisArg and called with three arguments: (value, index | key, collection).

If the predicate specifies the name of the property created by _.property The callback style returns the property value of this element.

If a value is also specified for thisArg, then creating a _.matchesProperty style callback returns true for elements that have the corresponding property value, otherwise false.

If an object is provided for the predicate, the created _.matches style callback returns true for elements that have the properties of this object, otherwise false. Arguments

  1. collection (Array | Object | string): collection to repeat.
  2. [Predicate = _. identity] (Function | Object | string): the function is called in one iteration.
  3. [thisArg] (*): this predicate binding.

Returns

(Array): returns an array of grouped elements.

More examples

 var users = [ { 'user': 'barney', 'age': 36, 'active': false }, { 'user': 'fred', 'age': 40, 'active': true }, { 'user': 'pebbles', 'age': 1, 'active': false } ]; var mapper = function(array) { return _.pluck(array, 'user'); }; // using the '_.matches' callback shorthand _.map(_.partition(users, { 'age': 1, 'active': false }), mapper); // → [['pebbles'], ['barney', 'fred']] // using the '_.matchesProperty' callback shorthand _.map(_.partition(users, 'active', false), mapper); // → [['barney', 'pebbles'], ['fred']] // using the '_.property' callback shorthand _.map(_.partition(users, 'active'), mapper); // → [['fred'], ['barney', 'pebbles']] 
+3
source

 function test(array, predicate){ var filterTrue = array.filter(predicate); var filterFalse = array.filter(function(data) { return !predicate(data); }) return { filterTrue: filterTrue, filterFalse: filterFalse } } mya = [ { id:1, desc: "one" }, { id:2, desc: "two" }, { id:3, desc: "three" }]; console.info(test(mya, function(data) { return data.id == 1; })) 

cancel the predicate.

0
source

Source: https://habr.com/ru/post/984014/


All Articles