Modo migliore per sommare un valore di proprietà in un array


184

Ho qualcosa del genere:

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

Ora per avere una quantità totale di questo array sto facendo qualcosa del genere:

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

È facile quando è solo un array, ma ho altri array con un nome di proprietà diverso che vorrei sommare.

Sarei più felice se potessi fare qualcosa del genere:

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

Ma non so come affrontarlo in modo da poterlo riutilizzare in futuro in questo modo:

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

Risposta

Ho deciso di utilizzare il suggerimento @ gruff-bunny, quindi evito di prototipare l'oggetto nativo (array)

Ho appena apportato una piccola modifica alla sua risposta convalidando l'array e il valore da sommare non è nullo, questa è la mia implementazione 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
Puoi pubblicare la tua risposta come risposta .
Ken Kin,

Risposte:


124

Risposta aggiornata

A causa di tutti gli svantaggi dell'aggiunta di una funzione al prototipo dell'array, sto aggiornando questa risposta per fornire un'alternativa che mantenga la sintassi simile alla sintassi originariamente richiesta nella domanda.

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

Risposta originale

Poiché si tratta di un array, è possibile aggiungere una funzione al prototipo di 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"))

The Fiddle: http://jsfiddle.net/9BAmj/


1
grazie @ sp00m ora ho modificato la mia implementazione con array.reduce proprio come ha risposto gruff-bunny.
nramirez,

Potrebbe essere necessario eseguire il polyfill della funzione di riduzione se è necessario supportare browser meno recenti: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bottens

Si prega di non estendere Array.prototypein quanto potrebbe violare il codice in futuro. Perché l'estensione degli oggetti nativi è una cattiva pratica? .
str

@bottens cosa succede se nell'array è presente un oggetto null?
Obby, l'

228

So che questa domanda ha una risposta accettata ma ho pensato di aggiungere una alternativa che utilizza array.reduce , visto che sommare un array è l'esempio canonico per ridurre:

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

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

Fiddle


risposta brillante, è possibile mettere $scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');all'inizio della funzione controller?
Mo.

Sì se viene dopo la creazione della funzione di somma.
Gruff Bunny,

14
Qual è il motivo del downvote del 6 ottobre 2015? In caso di problemi con la risposta, aggiungi un commento e lo correggerò. Nessuno impara da voti anonimi.
Gruff Bunny,

Ciò può comportare NaN, se il prop non esiste (non definito) in uno degli oggetti dell'array. Non so se questo è il miglior controllo, ma ha funzionato nel mio caso: function (items, prop) {return items.reduce (function (a, b) {if (! IsNaN (b [prop])) { return a + b [prop];} else {return a}}, 0); }
claytronicon,

131

Solo un altro esempio, questo è ciò per cui nativeJavaScript è stato creato Mape per cui Reducesono state create (Map e Reduce sono potenti in molte lingue).

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

Nota : creando funzioni più piccole separate abbiamo la possibilità di riutilizzarle.

// 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);per verificare solo il numero
diEcho il

4
Discuterei solo con il nome della variabile prevnella funzione di riduzione. Per me, ciò implica che stai ottenendo il valore precedente nella matrice. Ma è davvero la riduzione di tutti i valori precedenti. Mi piace accumulator, aggregatoro sumSoFarper chiarezza.
carpiediem

93

Evito sempre di cambiare il metodo prototipo e di aggiungere una libreria, quindi questa è la mia soluzione:

L'uso del metodo prototipo di array ridotto è sufficiente

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

3
Il secondo parametro (che determina il valore iniziale di a) fa il trucco quando si usa reducecon gli oggetti: nel primo iteratore, anon sarà il primo oggetto itemsdell'array, invece lo sarà 0.
Parziphal,

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

2
Come programmatore della vecchia scuola, la reducesintassi mi sembra totalmente ottusa. Il mio cervello ancora "nativamente" lo capisce meglio:let total = 0; items.forEach((item) => { total += item.price; });
AndrWeisR

1
@AndrWeisR Mi piace di più la tua soluzione. Anche l'uso di nativo in quanto è una parola così vivace che difficilmente viene spiegato a cosa serve. Ho creato una riga di codice ancora più semplice in base alla tua soluzione e ha funzionato alla grande. let total = 0; items.forEach(item => total += item.price)
Ian Poston Framer,

22

Ho pensato di perdere i miei due centesimi su questo: questa è una di quelle operazioni che dovrebbero sempre essere puramente funzionali, non basarsi su variabili esterne. Alcuni hanno già dato una buona risposta, usandoreduce è il modo di andare qui.

Poiché la maggior parte di noi può già permettersi di utilizzare la sintassi ES2015, ecco la mia proposta:

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

La stiamo rendendo una funzione immutabile mentre ci siamo. Che cosareduce sta facendo qui è semplicemente questo: Inizia con un valore di 0per l'accumulatore e aggiungi ad esso il valore dell'elemento loop corrente.

Sì per la programmazione funzionale e ES2015! :)


22

Alternativa per una migliore leggibilità e utilizzo Mape 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);

Funzione riutilizzabile:

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

Compagno perfetto, "+1"
Mayank Pandeyz,

Usa riduci valore predefinito .reduce(*** => *** + ***, 0);mancava ad altri che "+1"
FDisk il

19

Puoi fare quanto segue:

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

13

Funziona per me in TypeScripte 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);

Spero sia utile


4

Non sono sicuro che questo sia già stato menzionato. Ma esiste una funzione lodash per questo. Lo snippet di seguito in cui valore è l'attributo da sommare è "valore".

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

Entrambi funzioneranno.



1

Ecco una riga che usa le funzioni freccia 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

Come sommare la matrice di oggetti usando 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

Dall'array di oggetti

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

Stavo già usando jquery. Ma penso che sia abbastanza intuitivo da avere solo:

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

Questa è fondamentalmente solo una versione abbreviata della risposta di @ akhouri.


0

Ecco una soluzione che trovo più flessibile:

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

Per ottenere la somma, basta usarla così:

let sum = sumOfArrayWithParameter(someArray, 'someProperty');
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.