Mejor manera de sumar un valor de propiedad en una matriz


184

Tengo algo como esto:

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

Ahora para tener una cantidad total de esta matriz, estoy haciendo algo como esto:

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

Es fácil cuando solo hay una matriz, pero tengo otras matrices con un nombre de propiedad diferente que me gustaría sumar.

Sería más feliz si pudiera hacer algo como esto:

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

Pero no sé cómo pasar por esto de una manera que pueda reutilizarlo en el futuro de esta manera:

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

Responder

Decidí usar la sugerencia de @ gruff-bunny, así que evito crear prototipos de objetos nativos (Array)

Acabo de hacer una pequeña modificación a su respuesta validando la matriz y el valor para sumar no es nulo, esta es mi implementación final:

$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
Puede publicar su respuesta como respuesta .
Ken Kin

Respuestas:


124

Respuesta actualizada

Debido a todas las desventajas de agregar una función al prototipo de matriz, estoy actualizando esta respuesta para proporcionar una alternativa que mantenga la sintaxis similar a la sintaxis solicitada originalmente en la pregunta.

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

Respuesta original

Como es una matriz, puede agregar una función al prototipo de matriz.

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

El violín: http://jsfiddle.net/9BAmj/


1
gracias @ sp00m ahora he cambiado mi implementación con array.reduce tal como respondió gruff-bunny
nramirez

Es posible que deba rellenar la función de reducción si necesita admitir navegadores antiguos: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bottens el

No extienda, Array.prototypeya que podría romper su código en el futuro. ¿Por qué extender objetos nativos es una mala práctica? .
str

@bottens, ¿qué pasa si hay un objeto nulo en la matriz?
Obby

228

Sé que esta pregunta tiene una respuesta aceptada, pero pensé en incorporar una alternativa que usa array.reduce , ya que sumar una matriz es el ejemplo canónico para reducir:

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

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

Fiddle


respuesta brillante, ¿es posible poner $scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');al principio de la función del controlador?
Lun.

Sí, si viene después de crear la función de suma.
Gruff Bunny

14
¿Alguna razón para el voto negativo el 6 de octubre de 2015? Si hay un problema con la respuesta, agregue un comentario y lo corregiré. Nadie aprende de votos negativos anónimos.
Gruff Bunny

Esto puede resultar en NaN, si el accesorio no existe (indefinido) en uno de los objetos en la matriz. No sé si esta es la mejor verificación, pero funcionó para mi caso: function (items, prop) {return items.reduce (function (a, b) {if (! IsNaN (b [prop]))) { devuelve a + b [prop];} else {return a}}, 0); }
claytronicon

131

Solo otra toma, esto es para lo que nativefunciona JavaScript Mapy para lo que Reducefueron construidas (Map and Reduce son potencias en muchos idiomas).

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 : al hacer funciones más pequeñas por separado, tenemos la capacidad de usarlas nuevamente.

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

44
return Number(item.Amount);para verificar solo el número
diEcho

44
Solo discutiría con el nombre de la variable preven la función reducir. Para mí, eso implica que está obteniendo el valor anterior en la matriz. Pero realmente es la reducción de todos los valores anteriores. Me gusta accumulator, aggregatoro sumSoFarpor claridad.
carpiediem

93

Siempre evito cambiar el método del prototipo y agregar la biblioteca, así que esta es mi solución:

Usar el método de prototipo de reducción de matriz es suficiente

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

3
El segundo parámetro (que determina el valor inicial de a) hace el truco cuando se usa reducecon objetos: en la primera iteración, ano será el primer objeto de la itemsmatriz, sino que lo será 0.
Parziphal

Como una función: const sumBy = (items, prop) => items.reduce((a, b) => +a + +b[prop], 0); Uso:sumBy(traveler, 'Amount') // => 235
Rafi

2
Como programador de la vieja escuela, la reducesintaxis me parece totalmente obtusa. Mi cerebro todavía "nativamente" entiende esto mejor:let total = 0; items.forEach((item) => { total += item.price; });
AndrWeisR

1
@AndrWeisR Me gusta más tu solución. También el uso de nativo, ya que es una palabra de moda que apenas se explica para qué sirve. Hice una línea de código aún más básica basada en su solución y funcionó muy bien. let total = 0; items.forEach(item => total += item.price)
Ian Poston Framer

22

Pensé en dejar mis dos centavos en esto: esta es una de esas operaciones que siempre debe ser puramente funcional, no depender de ninguna variable externa. Algunos ya dieron una buena respuesta, usandoreduce es el camino a seguir aquí.

Como la mayoría de nosotros ya podemos permitirnos usar la sintaxis ES2015, esta es mi propuesta:

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

Lo estamos haciendo una función inmutable mientras estamos en ello. Quéreduce está haciendo aquí es simplemente esto: comience con un valor de 0para el acumulador y agregue el valor del elemento en bucle actual.

¡Yay para programación funcional y ES2015! :)


22

Alternativa para mejorar la legibilidad y el uso Mapy 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);

Función reutilizable:

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

Compañero perfecto, "+1"
Mayank Pandeyz

Use reducir el valor predeterminado a .reduce(*** => *** + ***, 0);otros le faltaba ese "+1"
FDisk

19

Puedes hacer lo siguiente:

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

13

Me está funcionando en TypeScripty 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);

Espero sea de utilidad.


4

No estoy seguro de que esto haya sido mencionado todavía. Pero hay una función lodash para eso. El fragmento debajo de donde el valor es su atributo para sumar es 'valor'.

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

Ambos funcionarán.



1

Aquí hay una línea usando las funciones de flecha 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

Cómo sumar una matriz de objetos 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

De una matriz de objetos

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

Ya estaba usando jquery. Pero creo que es lo suficientemente intuitivo como para tener:

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

Esto es básicamente una versión abreviada de la respuesta de @ akhuri.


0

Aquí hay una solución que encuentro más 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;
}

Para obtener la suma, simplemente úsela así:

let sum = sumOfArrayWithParameter(someArray, 'someProperty');
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.