Кращий спосіб підсумувати значення властивості в масиві


184

У мене є щось подібне:

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

Тепер, щоб мати загальну кількість цього масиву, я роблю щось подібне:

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

Легко, коли це лише один масив, але у мене є інші масиви з іншою назвою властивості, які я хотів би підбити.

Я був би щасливішим, якби я міг зробити щось подібне:

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

Але я не знаю, як це пережити таким чином, щоб я міг його повторно використовувати в майбутньому так:

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

Відповідь

Я вирішив використовувати пропозицію @ gruff-bunny, тому уникаю прототипування власного об'єкта (масив)

Я щойно зробив невелику модифікацію його відповіді, що перевіряє масив і значення для підсумовування не є нульовим, це моя остаточна реалізація:

$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
Ви можете опублікувати свою відповідь як відповідь .
Кен Кін

Відповіді:


124

Оновлений відповідь

Через всі недоліки додавання функції до прототипу Array, я оновлюю цю відповідь, щоб забезпечити альтернативу, яка зберігає синтаксис подібним до синтаксису, який спочатку запитували у питанні.

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

Оригінальний відповідь

Оскільки це масив, ви можете додати функцію до прототипу масиву.

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

Скрипка: http://jsfiddle.net/9BAmj/


1
дякую @ sp00m зараз я змінив свою реалізацію з array.reduce так, як відповів gruff-bunny.
nramirez

Можливо, вам доведеться поліфікувати функцію зменшення, якщо вам потрібно підтримувати старіші веб-переглядачі: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
боттенс

Не продовжуйте, Array.prototypeоскільки це може зламати ваш код у майбутньому. Чому розширення рідних об’єктів є поганою практикою? .
вул

@bottens, що якщо в масиві є нульовий об'єкт?
Obby

228

Я знаю, що на це питання є прийнята відповідь, але я подумав, що я підключуюся до альтернативи, яка використовує array.reduce , бачачи, що підсумовування масиву є канонічним прикладом для зменшення:

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

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

Fiddle


блискуча відповідь, чи можна поставити $scope.travelerTotal = $scope.sum($scope.traveler, 'Amount');на початку функції контролера?
Понеділок

Так, якщо це відбувається після створення функції суми.
Gruff Bunny

14
Будь-яка причина для голосування 6 жовтня 2015 року? Якщо є проблема з відповіддю, будь ласка, додайте коментар, і я його виправлю. Ніхто не дізнається з анонімних голосів.
Gruff Bunny

Це може призвести до NaN, якщо опора не існує (не визначена) в одному з об’єктів масиву. Я не знаю, чи це найкраща перевірка, але вона працювала в моєму випадку: function (items, prop) {return items.reduce (function (a, b) {if (! IsNaN (b [prop])) { повернути a + b [prop];} else {return a}}, 0); }
келітронікон

129

Ще один прийом, саме для цього і були побудовані nativeфункції JavaScript (Map and Reduce - це електростанції на багатьох мовах).MapReduce

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

Примітка : роблячи окремі менші функції, ми отримуємо можливість їх знову використовувати.

// 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);перевірити лише на номер
diEcho

4
Я б сперечався лише з назвою змінної prevу функції зменшення. Для мене це означає, що ви отримуєте попереднє значення в масиві. Але насправді це зменшення всіх попередніх значень. Мені подобається accumulator, aggregatorабо sumSoFarдля наочності.
carpiediem

93

Я завжди уникаю зміни методу прототипу та додавання бібліотеки, тому це моє рішення:

Використання методу зменшення прототипу Array достатньо

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

3
Другий параметр (який визначає початкове значення a) виконує трюк при використанні reduceз об'єктами: у першому ітераторі aне буде першим об'єктом itemsмасиву, натомість він буде 0.
Парцифал

Як функція: const sumBy = (items, prop) => items.reduce((a, b) => +a + +b[prop], 0); Використання:sumBy(traveler, 'Amount') // => 235
Рафі

2
Як старий шкільний програміст, reduceсинтаксис мені виглядає абсолютно тупо. Мій мозок все ще «по-новому» розуміє це краще:let total = 0; items.forEach((item) => { total += item.price; });
AndrWeisR

1
@AndrWeisR мені найкраще подобається ваше рішення. Також використання рідного, оскільки це таке гучне слово, яке майже не пояснюється, для чого воно призначене. Я зробив ще більш базовий рядок коду на основі вашого рішення, і він чудово працював. let total = 0; items.forEach(item => total += item.price)
Ian Poston Framer

22

Я думав, що я скину два центи на це: це одна з тих операцій, яка завжди повинна бути чисто функціональною, не покладаючись на будь-які зовнішні змінні. Кілька вже дали хорошу відповідь, використовуючи reduceспосіб поїхати сюди.

Оскільки більшість із нас вже може дозволити собі використовувати синтаксис ES2015, ось моя пропозиція:

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

Ми робимо це незмінною функцією, поки ми в ній. Що reduceтут робиться, це просто: Почніть зі значення 0для акумулятора і додайте до нього значення поточного певного елемента.

Так, для функціонального програмування та ES2015! :)


22

Альтернатива для покращення читабельності та використання Mapта 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);

Функція повторного використання:

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

Ідеальний товариш, "+1"
Mayank Pandeyz

Використовуйте зменшити значення за замовчуванням, а .reduce(*** => *** + ***, 0);іншим не вистачало значення "+1"
FDisk

19

Ви можете зробити наступне:

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

13

Це працює для мене TypeScriptі 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);

Я сподіваюся, що це корисно.


4

Я не впевнений, що про це вже говорилося. Але для цього є функція lodash . Фрагмент нижче, де значенням є ваш атрибут до суми, є "значення".

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

Обидва будуть працювати.



1

Ось однолінійний з використанням функцій стрілок 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

Як підсумовувати масив об'єкта за допомогою 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

З масиву об’єктів

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

Я вже використовував jquery. Але я думаю, що це досить інтуїтивно, щоб просто мати:

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

Це в основному лише коротка версія відповіді @ akhouri.


0

Ось рішення, яке я вважаю більш гнучким:

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

Щоб отримати суму, просто використовуйте її так:

let sum = sumOfArrayWithParameter(someArray, 'someProperty');
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.