web-dev-qa-db-fra.com

Mapper et filtrer un tableau en même temps en Javascript

Je souhaite parcourir un tableau d'objets pour créer un nouveau tableau filtré. Mais aussi, j'ai besoin de filtrer certains des objets du nouveau tableau en fonction d'un paramètre. J'essaye ceci:

function renderOptions(options) {
    return options.map(function (option) {
        if (!option.assigned) {
            return (someNewObject);
        }
    });   
}

Est-ce une bonne approche? Y a-t-il une meilleure méthode? Je suis ouvert à utiliser n'importe quelle bibliothèque telle que lodash.

61

Vous devriez utiliser Array.reduce pour cela.

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

var reduced = options.reduce(function(filtered, option) {
  if (option.assigned) {
     var someNewValue = { name: option.name, newProperty: 'Foo' }
     filtered.Push(someNewValue);
  }
  return filtered;
}, []);

document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>

Plus d'infos: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

95
thefourtheye

Utilisez réduire, Luke!

function renderOptions(options) {
    return options.reduce(function (res, option) {
        if (!option.assigned) {
            res.Push(someNewObject);
        }
        return res;
    }, []);   
}
35
Zuker

Une ligne reduce avec ES6 fancy syntaxe répandue est ici! 

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

const filtered = options
  .reduce((result, {name, assigned}) => [...result, ...assigned ? [name] : []], []);

console.log(filtered);

5
Maxim Kuzmin

Utilisez Array.prototype.filter même

function renderOptions(options) {
    return options.filter(function(option){
        return !option.assigned;
    }).map(function (option) {
        return (someNewObject);
    });   
}
2
AmmarCSE

Array.prototype.flatMap est une autre option.

const options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];
options.flatMap(o => o.assigned && o.name || []);
// ['One', 'Three']

De la page MDN liée ci-dessus:

flatMap peut être utilisé pour ajouter et supprimer des éléments (modifier le nombre d'éléments) au cours d'une carte. En d'autres termes, cela vous permet de mapper de nombreux éléments sur de nombreux éléments (en traitant chaque élément d'entrée séparément), plutôt que toujours un à un. En ce sens, cela fonctionne comme le contraire du filtre. Renvoyez simplement un tableau à 1 élément pour conserver l'élément, un tableau à plusieurs éléments pour ajouter des éléments ou un tableau à 0 éléments pour supprimer l'élément.

2
Trevor Dixon

J'ai optimisé les réponses avec les points suivants:

  1. Réécriture de if (cond) { stmt; } en cond && stmt;
  2. Utiliser ES6 Fonctions de flèche

Je présenterai deux solutions, l'une utilisant pourChaque , l'autre utilisant réduire :

Solution 1: Utiliser forEach

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];
var reduced = []
options.forEach(o => {
  o.assigned && reduced.Push( { name: o.name, newProperty: 'Foo' } );
} );
console.log(reduced);

Solution 2: Utiliser réduire

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];
var reduced = options.reduce((a, o) => {
  o.assigned && a.Push( { name: o.name, newProperty: 'Foo' } );
  return a;
}, [ ] );
console.log(reduced);

Je vous laisse le soin de décider quelle solution choisir.

1
Stephen Quan

À l’aide de réduction, vous pouvez le faire dans une fonction Array.prototype. Cela va chercher tous les nombres pairs d'un tableau.

var arr = [1,2,3,4,5,6,7,8];

var brr  = arr.reduce(function(c,n){
  if(n%2!=0){
       return c;
    }
  c.Push(n);
  return c;

},[]);

document.getElementById('mypre').innerHTML = brr.toString();
<h1>Get all even numbers</h1>
<pre id="mypre"> </pre>

Vous pouvez utiliser la même méthode et la généraliser pour vos objets, comme ceci.

var arr = options.reduce(function(c,n){
  if(somecondition){return c;}
  c.Push(n); return c;
},[]);

arr va maintenant contenir les objets filtrés.

1
Bhargav Ponnapalli

Je ferais un commentaire, mais je n'ai pas la réputation requise. Une petite amélioration à la très bonne réponse de Maxim Kuzmin pour le rendre plus efficace:

const options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

const filtered = options
  .reduce((result, { name, assigned }) => assigned ? result.concat(name) : result, []);

console.log(filtered);

Explication

Au lieu de répéter le résultat entier à chaque itération, nous ajoutons uniquement au tableau, et uniquement lorsqu'il existe une valeur à insérer.

0
matsve

À un moment donné, n’est-il pas plus facile (ou tout aussi facile) d’utiliser une variable forEach

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

var reduced = []
options.forEach(function(option) {
  if (option.assigned) {
     var someNewValue = { name: option.name, newProperty: 'Foo' }
     reduced.Push(someNewValue);
  }
});

document.getElementById('output').innerHTML = JSON.stringify(reduced);
<h1>Only assigned options</h1>
<pre id="output"> </pre>

Cependant, ce serait bien s'il y avait une fonction malter() ou fap() qui combine les fonctions map et filter. Cela fonctionnerait comme un filtre, sauf qu'au lieu de retourner vrai ou faux, cela retournerait n'importe quel objet ou un null/undefined.

0
Daniel