Meilleure façon de additionner une valeur de propriété dans un tableau


184

J'ai quelque chose comme ça:

$scope.traveler = [
            {  description: 'Senior', Amount: 50},
            {  description: 'Senior', Amount: 50},
            {  description: 'Adult', Amount: 75},
            {  description: 'Child', Amount: 35},
            {  description: 'Infant', Amount: 25 },
];

Maintenant, pour avoir un montant total de ce tableau, je fais quelque chose comme ceci:

$scope.totalAmount = function(){
       var total = 0;
       for (var i = 0; i < $scope.traveler.length; i++) {
              total = total + $scope.traveler[i].Amount;
            }
       return total;
}

C'est facile lorsqu'il n'y a qu'un seul tableau, mais j'ai d'autres tableaux avec un nom de propriété différent que je voudrais additionner.

Je serais plus heureux si je pouvais faire quelque chose comme ça:

$scope.traveler.Sum({ Amount });

Mais je ne sais pas comment passer par là de manière à pouvoir le réutiliser à l'avenir comme ceci:

$scope.someArray.Sum({ someProperty });

Répondre

J'ai décidé d'utiliser la suggestion @ gruff-bunny, donc j'évite de prototyper un objet natif (Array)

Je viens de faire une petite modification à sa réponse en validant le tableau et la valeur à sum n'est pas nulle, c'est ma mise en œuvre finale:

$scope.sum = function (items, prop) {
    if (items == null) {
        return 0;
    }
    return items.reduce(function (a, b) {
        return b[prop] == null ? a : a + b[prop];
    }, 0);
};

2
Vous pouvez publier votre réponse comme réponse .
Ken Kin

Réponses:


124

Réponse mise à jour

En raison de tous les inconvénients de l'ajout d'une fonction au prototype Array, je mets à jour cette réponse pour fournir une alternative qui garde la syntaxe similaire à la syntaxe initialement demandée dans la question.

class TravellerCollection extends Array {
    sum(key) {
        return this.reduce((a, b) => a + (b[key] || 0), 0);
    }
}
const traveler = new TravellerCollection(...[
    {  description: 'Senior', Amount: 50},
    {  description: 'Senior', Amount: 50},
    {  description: 'Adult', Amount: 75},
    {  description: 'Child', Amount: 35},
    {  description: 'Infant', Amount: 25 },
]);

console.log(traveler.sum('Amount')); //~> 235

Réponse originale

Puisqu'il s'agit d'un tableau, vous pouvez ajouter une fonction au prototype Array.

traveler = [
    {  description: 'Senior', Amount: 50},
    {  description: 'Senior', Amount: 50},
    {  description: 'Adult', Amount: 75},
    {  description: 'Child', Amount: 35},
    {  description: 'Infant', Amount: 25 },
];

Array.prototype.sum = function (prop) {
    var total = 0
    for ( var i = 0, _len = this.length; i < _len; i++ ) {
        total += this[i][prop]
    }
    return total
}

console.log(traveler.sum("Amount"))

Le violon: http://jsfiddle.net/9BAmj/


1
merci @ sp00m maintenant j'ai changé mon implémentation avec array.reduce tout comme gruff-bunny a répondu.
nramirez

Vous devrez peut-être polyfill la fonction de réduction si vous avez besoin de prendre en charge des navigateurs plus anciens: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference
...

Veuillez ne pas prolonger Array.prototypecar cela pourrait casser votre code à l'avenir. Pourquoi l'extension des objets natifs est-elle une mauvaise pratique? .
str

@bottens et s'il y a un objet nul dans le tableau?
Obby le

228

Je sais que cette question a une réponse acceptée, mais j'ai pensé que je ferais partie d'une alternative qui utilise array.reduce , car la sommation d'un tableau est l'exemple canonique pour réduire:

$scope.sum = function(items, prop){
    return items.reduce( function(a, b){
        return a + b[prop];
    }, 0);
};

$scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');

Fiddle


réponse brillante, est-il possible de mettre $scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');au début de la fonction de contrôleur?
Lun

Oui s'il survient après la création de la fonction de somme.
Gruff Bunny

14
Y a-t-il une raison pour le vote défavorable du 6 octobre 2015? S'il y a un problème avec la réponse, veuillez ajouter un commentaire et je le corrigerai. Personne n'apprend des votes négatifs anonymes.
Gruff Bunny

Cela peut entraîner NaN, si le prop n'existe pas (non défini) dans l'un des objets du tableau. Je ne sais pas si c'est la meilleure vérification, mais cela a fonctionné pour mon cas: function (items, prop) {return items.reduce (function (a, b) {if (! IsNaN (b [prop])) { return a + b [prop];} else {return a}}, 0); }
claytronicon

131

Juste une autre prise, c'est ce pour quoi nativeJavaScript fonctionne Mapet a Reduceété conçu (Map et Reduce sont des moteurs dans de nombreuses langues).

var traveler = [{description: 'Senior', Amount: 50},
                {description: 'Senior', Amount: 50},
                {description: 'Adult', Amount: 75},
                {description: 'Child', Amount: 35},
                {description: 'Infant', Amount: 25}];

function amount(item){
  return item.Amount;
}

function sum(prev, next){
  return prev + next;
}

traveler.map(amount).reduce(sum);
// => 235;

// or use arrow functions
traveler.map(item => item.Amount).reduce((prev, next) => prev + next);

Remarque : en créant des fonctions plus petites séparées, nous avons la possibilité de les utiliser à nouveau.

// Example of reuse.
// Get only Amounts greater than 0;

// Also, while using Javascript, stick with camelCase.
// If you do decide to go against the standards, 
// then maintain your decision with all keys as in...

// { description: 'Senior', Amount: 50 }

// would be

// { Description: 'Senior', Amount: 50 };

var travelers = [{description: 'Senior', amount: 50},
                {description: 'Senior', amount: 50},
                {description: 'Adult', amount: 75},
                {description: 'Child', amount: 35},
                {description: 'Infant', amount: 0 }];

// Directly above Travelers array I changed "Amount" to "amount" to match standards.

function amount(item){
  return item.amount;
}

travelers.filter(amount);
// => [{description: 'Senior', amount: 50},
//     {description: 'Senior', amount: 50},
//     {description: 'Adult', amount: 75},
//     {description: 'Child', amount: 35}];
//     Does not include "Infant" as 0 is falsey.

4
return Number(item.Amount);pour vérifier le numéro seulement
diEcho

4
Je ne discuterais qu'avec le nom de la variable prevdans la fonction de réduction. Pour moi, cela implique que vous obtenez la valeur précédente dans le tableau. Mais c'est vraiment la réduction de toutes les valeurs précédentes. J'aime accumulator, aggregatorou sumSoFarpour plus de clarté.
carpiediem

93

J'évite toujours de changer de méthode de prototype et d'ajouter une bibliothèque, c'est donc ma solution:

L'utilisation de la méthode de prototype de réduction de matrice est suffisante

// + operator for casting to Number
items.reduce((a, b) => +a + +b.price, 0);

3
Le deuxième paramètre (qui détermine la valeur initiale de a) fait l'affaire lors de l'utilisation reduceavec des objets: dans la première itération, ane sera pas le premier objet du itemstableau, à la place il le sera 0.
Parziphal

En fonction: const sumBy = (items, prop) => items.reduce((a, b) => +a + +b[prop], 0); Utilisation:sumBy(traveler, 'Amount') // => 235
Rafi

2
En tant que programmeur old school, la reducesyntaxe me semble totalement obtuse. Mon cerveau comprend toujours mieux cela "nativement":let total = 0; items.forEach((item) => { total += item.price; });
AndrWeisR

1
@AndrWeisR J'aime mieux votre solution. De plus, l'utilisation de natif car c'est un mot à la mode qui est à peine expliqué à quoi il sert. J'ai créé une ligne de code encore plus basique basée sur votre solution et cela a très bien fonctionné. let total = 0; items.forEach(item => total += item.price)
Ian Poston Framer

22

J'ai pensé que je laisserais tomber mes deux cents là-dessus: c'est l'une de ces opérations qui devrait toujours être purement fonctionnelle, ne reposant sur aucune variable externe. Quelques-uns ont déjà donné une bonne réponse, en utilisantreduce est la voie à suivre ici.

Étant donné que la plupart d'entre nous peuvent déjà se permettre d'utiliser la syntaxe ES2015, voici ma proposition:

const sumValues = (obj) => Object.keys(obj).reduce((acc, value) => acc + obj[value], 0);

Nous en faisons une fonction immuable tant que nous y sommes. Ce qui reduceest fait ici est simplement ceci: Commencez par une valeur de0 pour l'accumulateur et ajoutez-y la valeur de l'élément en boucle actuel.

Yay pour la programmation fonctionnelle et ES2015! :)


22

Alternative pour une meilleure lisibilité et utilisation Mapet Reduce:

const traveler = [
    {  description: 'Senior', amount: 50 },
    {  description: 'Senior', amount: 50 },
    {  description: 'Adult', amount: 75 },
    {  description: 'Child', amount: 35 },
    {  description: 'Infant', amount: 25 },
];

const sum = traveler
  .map(item => item.amount)
  .reduce((prev, curr) => prev + curr, 0);

Fonction réutilisable:

const calculateSum = (obj, field) => obj
  .map(items => items.attributes[field])
  .reduce((prev, curr) => prev + curr, 0);

Perfect mate, "+1"
Mayank Pandeyz

Utiliser la valeur par défaut de réduction d' .reduce(*** => *** + ***, 0);autres manquaient que "+1"
FDisk

19

Vous pouvez faire ce qui suit:

$scope.traveler.map(o=>o.Amount).reduce((a,c)=>a+c);

13

Cela fonctionne pour moi TypeScriptet JavaScript:

let lst = [
     { description:'Senior', price: 10},
     { description:'Adult', price: 20},
     { description:'Child', price: 30}
];
let sum = lst.map(o => o.price).reduce((a, c) => { return a + c });
console.log(sum);

J'espère que c'est utile.


4

Je ne suis pas sûr que cela ait encore été mentionné. Mais il y a une fonction lodash pour cela. Extrait ci-dessous où la valeur est votre attribut à additionner est «valeur».

_.sumBy(objects, 'value');
_.sumBy(objects, function(o) { return o.value; });

Les deux fonctionneront.



1

Voici un one-liner utilisant les fonctions fléchées ES6.

const sumPropertyValue = (items, prop) => items.reduce((a, b) => a + b[prop], 0);

// usage:
const cart_items = [ {quantity: 3}, {quantity: 4}, {quantity: 2} ];
const cart_total = sumPropertyValue(cart_items, 'quantity');

1

Comment additionner un tableau d'objets en utilisant Javascript

const traveler = [
  {  description: 'Senior', Amount: 50},
  {  description: 'Senior', Amount: 50},
  {  description: 'Adult', Amount: 75},
  {  description: 'Child', Amount: 35},
  {  description: 'Infant', Amount: 25 }
];

const traveler = [
    {  description: 'Senior', Amount: 50},
    {  description: 'Senior', Amount: 50},
    {  description: 'Adult', Amount: 75},
    {  description: 'Child', Amount: 35},
    {  description: 'Infant', Amount: 25 },
];
function sum(arrayData, key){
   return arrayData.reduce((a,b) => {
  return {Amount : a.Amount + b.Amount}
})
}
console.log(sum(traveler))
»


1

À partir d'un tableau d'objets

function getSum(array, column)
  let values = array.map((item) => parseInt(item[column]) || 0)
  return values.reduce((a, b) => a + b)
}

foo = [
  { a: 1, b: "" },
  { a: null, b: 2 },
  { a: 1, b: 2 },
  { a: 1, b: 2 },
]

getSum(foo, a) == 3
getSum(foo, b) == 6

0

J'utilisais déjà jquery. Mais je pense que c'est assez intuitif pour avoir:

var total_amount = 0; 
$.each(traveler, function( i, v ) { total_amount += v.Amount ; });

Ceci est essentiellement une version abrégée de la réponse de @ akhouri.


0

Voici une solution que je trouve plus flexible:

function sumOfArrayWithParameter (array, parameter) {
  let sum = null;
  if (array && array.length > 0 && typeof parameter === 'string') {
    sum = 0;
    for (let e of array) if (e && e.hasOwnProperty(parameter)) sum += e[parameter];
  }
  return sum;
}

Pour obtenir la somme, utilisez-la simplement comme ça:

let sum = sumOfArrayWithParameter(someArray, 'someProperty');
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.