À 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.

501
demandé sur Trent 2013-10-25 17:13:03

12 réponses

voici une façon plus courte de l'atteindre

var result = objArray.map(a => a.foo);
477
répondu Jalasem 2017-10-11 17:33:48

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);

Documentation

521
répondu Niet the Dark Absol 2018-02-10 19:57:41

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)
38
répondu cpdt 2017-09-28 06:07:54

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.

26
répondu pscl 2017-10-18 01:05:37

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.

14
répondu Alnitak 2013-10-25 13:15:16

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 ]

7
répondu Tejas Patel 2016-08-21 10:35:12

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;
    }, []);
}

jsbin exemple

ç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;
    }, []);
}

jsbin exemple

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;
    }, []);
}

jsbin exemple

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;
    }, []);
}

jsbin exemple

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 : []));
    }, []);
}

jsbin exemple

3
répondu Rogier Spieker 2016-11-19 10:02:14

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 :).

2
répondu sitifensys 2013-10-25 13:54:03

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]});

https://jsfiddle.net/ohea7mgk /

1
répondu Niko Gamulin 2015-11-28 16:52:17

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}]);
1
répondu TechWisdom 2018-04-07 15:48:57

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"]

1
répondu eugen sunic 2018-07-25 14:17:56

dans ES6, vous pouvez faire:

const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
1
répondu Yuxuan Chen 2018-08-23 00:15:56