J'ai un tableau Javascript que je voudrais diviser en deux selon que la fonction appelée sur chaque élément renvoie true
ou false
. Pour l' essentiel, c'est un array.filter
, mais je voudrais aussi avoir à portée de main les éléments qui ont été filtrés sur .
Actuellement, mon plan est d'utiliser array.forEach
et d'appeler la fonction de prédicat sur chaque élément. Selon que cela est vrai ou faux, je vais pousser l'élément actuel sur l'un des deux nouveaux tableaux. Existe-t-il une manière plus élégante ou meilleure de le faire? Un array.filter
où le poussera l'élément sur un autre tableau avant son retour false
, par exemple?
javascript
Mike Chen
la source
la source
.filter
mais ces effets secondaires sont difficiles à suivre et à comprendre. Il suffit de parcourir le tableau et de pousser vers un tableau ou un autre.Réponses:
Avec ES6, vous pouvez utiliser la syntaxe de diffusion avec réduire:
function partition(array, isValid) { return array.reduce(([pass, fail], elem) => { return isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]]; }, [[], []]); } const [pass, fail] = partition(myArray, (e) => e > 5);
Ou sur une seule ligne:
const [pass, fail] = a.reduce(([p, f], e) => (e > 5 ? [[...p, e], f] : [p, [...f, e]]), [[], []]);
la source
Vous pouvez utiliser lodash.partition
var users = [ { 'user': 'barney', 'age': 36, 'active': false }, { 'user': 'fred', 'age': 40, 'active': true }, { 'user': 'pebbles', 'age': 1, 'active': false } ]; _.partition(users, function(o) { return o.active; }); // → objects for [['fred'], ['barney', 'pebbles']] // The `_.matches` iteratee shorthand. _.partition(users, { 'age': 1, 'active': false }); // → objects for [['pebbles'], ['barney', 'fred']] // The `_.matchesProperty` iteratee shorthand. _.partition(users, ['active', false]); // → objects for [['barney', 'pebbles'], ['fred']] // The `_.property` iteratee shorthand. _.partition(users, 'active'); // → objects for [['fred'], ['barney', 'pebbles']]
ou ramda.partition
R.partition(R.contains('s'), ['sss', 'ttt', 'foo', 'bars']); // => [ [ 'sss', 'bars' ], [ 'ttt', 'foo' ] ] R.partition(R.contains('s'), { a: 'sss', b: 'ttt', foo: 'bars' }); // => [ { a: 'sss', foo: 'bars' }, { b: 'ttt' } ]
la source
Vous pouvez utiliser pour cela réduire:
function partition(array, callback){ return array.reduce(function(result, element, i) { callback(element, i, array) ? result[0].push(element) : result[1].push(element); return result; }, [[],[]] ); };
Mise à jour. En utilisant la syntaxe ES6, vous pouvez également le faire en utilisant la récursivité:
function partition([current, ...tail], f, [left, right] = [[], []]) { if(current === undefined) { return [left, right]; } if(f(current)) { return partition(tail, f, [[...left, current], right]); } return partition(tail, f, [left, [...right, current]]); }
la source
[...left, current]
ou[...right, current]
- pour chaque élément. Je ne connais pas les éléments internes exacts, mais je suis sûr que la construction coûte plus cher que de simplement pousser un élément sur un tableau. De plus, en règle générale, la récursivité est plus coûteuse que l' itération , car elle implique la création à chaque fois d'un "cadre de pile".Cela ressemble beaucoup à la
Enumerable#partition
méthode de Ruby .Si la fonction ne peut pas avoir d'effets secondaires (c'est-à-dire qu'elle ne peut pas modifier le tableau d'origine), alors il n'y a pas de moyen plus efficace de partitionner le tableau que d'itérer sur chaque élément et de pousser l'élément vers l'un de vos deux tableaux.
Cela étant dit, il est sans doute plus «élégant» de créer une méthode
Array
pour exécuter cette fonction. Dans cet exemple, la fonction de filtre est exécutée dans le contexte du tableau d'origine (c'est-à-dire,this
sera le tableau d'origine), et elle reçoit l'élément et l'index de l'élément en arguments (similaire à laeach
méthode de jQuery ):Array.prototype.partition = function (f){ var matched = [], unmatched = [], i = 0, j = this.length; for (; i < j; i++){ (f.call(this, this[i], i) ? matched : unmatched).push(this[i]); } return [matched, unmatched]; }; console.log([1, 2, 3, 4, 5].partition(function (n, i){ return n % 2 == 0; })); //=> [ [ 2, 4 ], [ 1, 3, 5 ] ]
la source
Je suis venu avec ce petit gars. Il utilise pour tout ce que vous avez décrit, mais il a l'air propre et succinct à mon avis.
//Partition function function partition(array, filter) { let pass = [], fail = []; array.forEach((e, idx, arr) => (filter(e, idx, arr) ? pass : fail).push(e)); return [pass, fail]; } //Run it with some dummy data and filter const [lessThan5, greaterThanEqual5] = partition([0,1,4,3,5,7,9,2,4,6,8,9,0,1,2,4,6], e => e < 5); //Output console.log(lessThan5); console.log(greaterThanEqual5);
la source
for
boucle comme dans une ancienne réponse de @qwertymk. Par exemple, pour un tableau contenant 100 000 éléments, c'était deux fois plus lent sur mon système.Dans la fonction de filtrage, vous pouvez pousser vos faux éléments dans une autre fonction extérieure variable:
var bad = [], good = [1,2,3,4,5]; good = good.filter(function (value) { if (value === false) { bad.push(value) } else { return true});
Bien sûr
value === false
faut être une vraie comparaison;)Mais il fait presque la même opération comme
forEach
. Je pense que vous devriez utiliserforEach
pour une meilleure lisibilité du code.la source
Facile à lire.
const partition = (arr, condition) => { const trues = arr.filter(el => condition(el)); const falses = arr.filter(el => !condition(el)); return [trues, falses]; }; // sample usage const nums = [1,2,3,4,5,6,7] const [evens, odds] = partition(nums, (el) => el%2 == 0)
la source
Essaye ça:
function filter(a, fun) { var ret = { good: [], bad: [] }; for (var i = 0; i < a.length; i++) if (fun(a[i]) ret.good.push(a[i]); else ret.bad.push(a[i]); return ret; }
DEMO
la source
Et ça?
[1,4,3,5,3,2].reduce( (s, x) => { s[ x > 3 ].push(x); return s;} , {true: [], false:[]} )
C'est probablement plus efficace que l'opérateur de propagation
Ou un peu plus court, mais plus laid
[1,4,3,5,3,2].reduce( (s, x) => s[ x > 3 ].push(x)?s:s , {true: [], false:[]} )
la source
Beaucoup de réponses ici utilisent
Array.prototype.reduce
pour construire un accumulateur mutable, et soulignent à juste titre que pour les grands tableaux, c'est plus efficace que, par exemple, d'utiliser un opérateur de propagation pour copier un nouveau tableau à chaque itération. L'inconvénient est que ce n'est pas aussi joli qu'une expression "pure" utilisant la courte syntaxe lambda.Mais un moyen de contourner cela est d'utiliser l'opérateur virgule. Dans les langages de type C, la virgule est un opérateur qui renvoie toujours l'opérande de droite. Vous pouvez l'utiliser pour créer une expression qui appelle une fonction void et renvoie une valeur.
function partition(array, predicate) { return array.reduce((acc, item) => predicate(item) ? (acc[0].push(item), acc) : (acc[1].push(item), acc), [[], []]); }
Si vous tirez parti du fait qu'une expression booléenne convertit implicitement un nombre comme 0 et 1, et vous pouvez la rendre encore plus concise, même si je ne pense pas qu'elle soit aussi lisible:
function partition(array, predicate) { return array.reduce((acc, item) => (acc[+!predicate(item)].push(item), acc), [[], []]); }
Usage:
const [trues, falses] = partition(['aardvark', 'cat', 'apple'], i => i.startsWith('a')); console.log(trues); // ['aardvark', 'apple'] console.log(falses); // ['cat']
la source
Partition ONE-LINER
const partition = (a,f)=>a.reduce((p,q)=>(p[+!f(q)].push(q),p),[[],[]]);
DEMO
// to make it consistent to filter pass index and array as arguments const partition = (a, f) => a.reduce((p, q, i, ar) => (p[+!f(q, i, ar)].push(q), p), [[], []]); console.log(partition([1, 2, 3, 4, 5], x => x % 2 === 0)); console.log(partition([..."ABCD"], (x, i) => i % 2 === 0));
Pour Typescript
const partition = <T>( a: T[], f: (v: T, i?: number, ar?: T[]) => boolean ): [T[], T[]] => a.reduce((p, q, i, ar) => (p[+!f(q, i, ar)].push(q), p), [[], []]);
la source
J'ai fini par faire cela parce que c'est facile à comprendre (et entièrement tapé avec dactylographié).
const partition = <T>(array: T[], isValid: (element: T) => boolean): [T[], T[]] => { const pass: T[] = [] const fail: T[] = [] array.forEach(element => { if (isValid(element)) { pass.push(element) } else { fail.push(element) } }) return [pass, fail] } // usage const [pass, fail] = partition([1, 2, 3, 4, 5], (element: number) => element > 3)
la source