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);
};
Nramirez
la source
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/

Bottens
la source
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

Lapin bourru
la source
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.
SoEzPz
la source
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);
Eric Marcelino
la source
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! :)

Ricardo Magalhães
la source
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);
codage
la source
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);
greenif
la source
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.

AminRostami
la source
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.

Liam Middleton
la source
2

peut également utiliser Array.prototype.forEach ()

let totalAmount = 0;
$scope.traveler.forEach( data => totalAmount = totalAmount + data.Amount);
return totalAmount;
Akhouri
la source
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');
Steve Breese
la source
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))
»

kvimalkr55
la source
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
Franz
la source
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.

SpiRail
la source
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');
Philippe Genois
la source