À partir d'un tableau d'objets, extraire la valeur d'une propriété comme tableau
j'ai le tableau objet JavaScript avec la structure suivante:
objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
je veux extraire un tableau contenant les valeurs de la clé foo
, résultant en une valeur de [ 1, 3, 5 ]
.
j'ai accompli ceci avec l'approche triviale, comme suit:
function getFields(input, field) {
var output = [];
for (var i=0; i < input.length ; ++i)
output.push(input[i][field]);
return output;
}
var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]
y a-t-il une méthode plus élégante et plus performante à accomplir?
duplicate suggéré , cette question demande comment convertir un objet en un tableau, cette question demande comment extraire une propriété de un tableau d'objets.
12 réponses
voici une façon plus courte de l'atteindre
var result = objArray.map(a => a.foo);
Oui, mais il s'appuie sur une fonctionnalité ES5 de JavaScript. Cela signifie qu'il ne fonctionnera pas dans IE8 ou plus vieux.
var result = objArray.map(function(a) {return a.foo;});
sur les interprètes JS compatibles ES6 vous pouvez utiliser un fonction de flèche pour la brièveté:
var result = objArray.map(a => a.foo);
Check out fonction de Lodash _.pluck()
1519190920" ou fonction de Underscore _.pluck()
. Les deux font exactement ce que vous voulez dans un seul appel de fonction!
var result = _.pluck(objArray, 'foo');
Update: _.pluck()
a été supprimé à partir de Lodash v4.0.0 , en faveur de _.map()
en combinaison avec quelque chose de similaire à Niet's answer . _.pluck()
est toujours disponible en Underscore .
mise à Jour 2: Marquer des points dans les commentaires , quelque part entre Lodash v4 et 4.3, une nouvelle fonction a été ajoutée, qui propose cette fonctionnalité de nouveau. _.property()
est une fonction de raccourci qui retourne une fonction pour obtenir la valeur d'une propriété dans un objet.
en outre, _.map()
permet maintenant de passer une chaîne de caractères comme second paramètre, qui est passé dans _.property()
. Par conséquent, les deux lignes suivantes sont équivalentes à l'échantillon de code ci-dessus de pre-Lodash 4.
var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));
_.property()
, et donc _.map()
, vous permettent également de fournir une chaîne ou un tableau séparé par des points afin d'accéder aux sous-Propriétés:
var objArray = [
{
someProperty: { aNumber: 5 }
},
{
someProperty: { aNumber: 2 }
},
{
someProperty: { aNumber: 9 }
}
];
var result = _.map(objArray, _.property('someProperty.aNumber'));
var result = _.map(objArray, _.property(['someProperty', 'aNumber']));
les deux appels _.map()
dans l'exemple ci-dessus retourneront [5, 2, 9]
.
si vous êtes un peu plus dans la programmation fonctionnelle, jetez un oeil à Ramda's R.pluck()
fonction, qui ressemblerait à quelque chose comme ceci:
var result = R.pluck('foo')(objArray); // or just R.pluck('foo', objArray)
parlant pour les solutions js only, j'ai trouvé que, aussi inélégant soit-il, une simple boucle indexée for
est plus performante que ses alternatives.
https://jsperf.com/extract-prop-from-object-array /
extraction d'une propriété simple à partir d'un tableau d'éléments 100000
traditionnel pour boucle 368 Ops /sec
var vals=[];
for(var i=0;i<testArray.length;i++){
vals.push(testArray[i].val);
}
ES6 for..de boucle 303 Ops /sec
var vals=[];
for(var item of testArray){
vals.push(item.val);
}
"1519130920 Tableau.prototype.carte 19 Ops /sec
var vals = testArray.map(function(a) {return a.val;});
Edit: Ops/s mise à jour 10/2017. TL; DR -.map () est lent. Mais parfois, la lisibilité vaut plus que la performance.
utilisant Array.prototype.map
:
function getFields(input, field) {
return input.map(function(o) {
return o[field];
});
}
voir le lien ci-dessus pour un shim pour les navigateurs pre-ES5.
il est préférable d'utiliser une sorte de bibliothèques comme lodash ou underscore pour l'assurance cross browser.
Dans lodash vous pouvez obtenir les valeurs d'une propriété dans le tableau en suivant la méthode
_.map(objArray,"foo")
et en underscore
_.pluck(objArray,"foo")
les deux sera de retour [ 1, 3, 5 ]
alors que map
est une bonne solution pour sélectionner "colonnes" d'une liste d'objets, il a un inconvénient. Si non vérifié explicitement si les colonnes existent ou non, il lancera une erreur et (au mieux) vous fournir undefined
.
J'opterais pour une solution reduce
, qui peut tout simplement ignorer la propriété ou même vous configurer avec une valeur par défaut.
function getFields(list, field) {
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// check if the item is actually an object and does contain the field
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
ça marcherait même si l'un des éléments dans la liste fournie n'est pas un objet ou ne contient pas le champ.
il peut même être rendu plus flexible en négociant une valeur par défaut si un élément n'est pas un objet ou ne contient pas le champ.
function getFields(list, field, otherwise) {
// reduce the provided list to an array containing either the requested field or the alternative value
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
Ce serait la même chose avec la carte, que la longueur du tableau retourné serait le même que le tableau. (Dans ce cas, un map
est un peu moins cher qu'un reduce
):
function getFields(list, field, otherwise) {
// map the provided list to an array containing either the requested field or the alternative value
return list.map(function(item) {
// If item is an object and contains the field, add its value and the value of otherwise if not
return typeof item === 'object' && field in item ? item[field] : otherwise;
}, []);
}
et puis il y a la solution la plus flexible, celle qui vous permet de passer d'un comportement à l'autre simplement en fournissant une valeur alternative.
function getFields(list, field, otherwise) {
// determine once whether or not to use the 'otherwise'
var alt = typeof otherwise !== 'undefined';
// reduce the provided list to an array only containing the requested field
return list.reduce(function(carry, item) {
// If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
if (typeof item === 'object' && field in item) {
carry.push(item[field]);
}
else if (alt) {
carry.push(otherwise);
}
// return the 'carry' (which is the list of matched field values)
return carry;
}, []);
}
comme les exemples ci-dessus (avec un peu de chance) éclairent sur la façon dont cela fonctionne, permet de raccourcir un peu la fonction en utilisant la fonction Array.concat
.
function getFields(list, field, otherwise) {
var alt = typeof otherwise !== 'undefined';
return list.reduce(function(carry, item) {
return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
}, []);
}
Cela dépend de votre définition de "mieux".
les autres réponses soulignent l'utilisation de la carte, qui est naturelle (en particulier pour les gars habitués au style fonctionnel) et concis. Je recommande fortement de l'utiliser (si vous ne vous embêtez pas avec les quelques gars IE8 - IT). Donc si "mieux" signifie "plus concis"," maintenable"," compréhensible " alors oui, c'est bien mieux.
d'autre part, cette beauté ne vient pas sans coûts supplémentaires. Je ne suis pas un grand fan de microbench, mais j'ai mis en place un petit test ici . Le résultat est prévisible, le vieux laid voie semble être plus rapide que la fonction map. Donc si "mieux" signifie "plus vite", alors non, restez à la mode de l'ancienne école.
encore une fois ce n'est qu'un microbench et en aucune façon de plaider contre l'utilisation de map
, c'est juste mes deux cents :).
fonction map est un bon choix lorsqu'il s'agit de tableaux d'objets. Bien qu'un certain nombre de bonnes réponses aient déjà été publiées, l'exemple de l'utilisation de map avec combinaison avec filtre pourrait être utile.
dans le cas où vous voulez exclure les propriétés dont les valeurs ne sont pas définies ou exclure juste une propriété spécifique, vous pouvez faire ce qui suit:
var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});
si vous voulez aussi prendre en charge des objets de type tableau, utilisez tableau.de (ES2015):
Array.from(arrayLike, x => x.foo);
L'avantage qu'il a sur "151990920 Tableau.prototype.map () méthode est l'entrée peut également être un ensemble :
let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
en général si vous voulez extrapoler des valeurs d'objet qui sont à l'intérieur d'un tableau (comme décrit dans la question) alors vous pouvez utiliser la réduction, la cartographie et la déstructuration de tableau.
ES6
let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
let b = a.reduce((acc, x) => [...acc, Object.values(x).map((y, i) => y)], []);
console.log(b)
l'équivalent en utilisant pour en boucle serait le suivant:
for (let i in a) {
let temp = [];
for (let j in a[i]) {
temp.push(a[i][j]);
}
array.push(temp);
}
Produits de sortie: ["mot", "nouveau", "certains", "1", "2", "3"]
dans ES6, vous pouvez faire:
const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)