วิธีที่ดีกว่าในการสรุปมูลค่าทรัพย์สินในอาร์เรย์


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 ดังนั้นฉันจึงหลีกเลี่ยงการสร้างต้นแบบวัตถุพื้นเมือง (Array)

ฉันเพิ่งแก้ไขคำตอบของเขาเพื่อตรวจสอบความถูกต้องของอาเรย์และมูลค่ารวมไม่เป็นโมฆะนี่คือการนำไปใช้ครั้งสุดท้าย

$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
คุณสามารถโพสต์ของคุณคำตอบเป็นคำตอบ
Ken Kin

คำตอบ:


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

คำตอบเดิม

เนื่องจากเป็นอาเรย์คุณจึงสามารถเพิ่มฟังก์ชั่นเข้ากับต้นแบบ 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
ขอบคุณ @ sp00m ตอนนี้ฉันมีการเปลี่ยนแปลงการใช้งานของฉันกับ array.reduce เช่นเดียวกับ gruff-bunny ตอบ
nramirez

คุณอาจต้อง polyfill ฟังก์ชั่นลดถ้าคุณต้องการที่จะสนับสนุนเบราว์เซอร์รุ่นเก่า: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

โปรดอย่าขยายArray.prototypeเนื่องจากอาจทำให้รหัสของคุณเสียหายในอนาคต เหตุใดการขยายวัตถุพื้นเมืองจึงเป็นการปฏิบัติที่ไม่ดี .
STR

@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');ที่จุดเริ่มต้นของฟังก์ชั่นควบคุมหรือไม่
Mo.

ใช่ถ้าเกิดขึ้นหลังจากสร้างฟังก์ชั่นผลรวม
Gruff Bunny

14
เหตุผลใดสำหรับการลงคะแนนในวันที่ 6 ตุลาคม 2015 หากมีปัญหาเกี่ยวกับคำตอบโปรดเพิ่มความคิดเห็นและฉันจะแก้ไขให้ถูกต้อง ไม่มีใครเรียนรู้จาก downvotes นิรนาม
Gruff Bunny

ซึ่งอาจส่งผลให้ NaN ถ้าไม่มีเสา (ไม่ได้กำหนด) ในวัตถุใดวัตถุหนึ่งในอาร์เรย์ ฉันไม่รู้ว่านี่เป็นการตรวจสอบที่ดีที่สุดหรือเปล่า แต่มันใช้ได้กับกรณีของฉัน: function (items, prop) {return items.reduce (function (a, b)) {if (! isNaN (b [prop])) { ส่งกลับ a + b [prop];} else {return a}}, 0); }
claytronicon

130

อีกสิ่งหนึ่งที่นี่คือสิ่งที่nativeฟังก์ชั่น JavaScript MapและReduceถูกสร้างขึ้นมาเพื่อ (แผนที่และลดขนาดเป็นแหล่งรวมพลังในหลายภาษา)

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

ฉันมักจะหลีกเลี่ยงการเปลี่ยนวิธีต้นแบบและเพิ่มไลบรารีดังนั้นนี่คือโซลูชันของฉัน:

การใช้วิธีลดอาเรย์ต้นแบบก็เพียงพอแล้ว

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

3
พารามิเตอร์ที่สอง (ซึ่งเป็นตัวกำหนดค่าเริ่มต้นของa) ไม่หลอกลวงเมื่อใช้reduceกับวัตถุ: ใน iteratorion แรกaจะไม่เป็นวัตถุแรกของอาร์เรย์แทนมันจะเป็นitems 0
Parziphal

ในฐานะที่เป็นฟังก์ชั่น: const sumBy = (items, prop) => items.reduce((a, b) => +a + +b[prop], 0); การใช้งาน:sumBy(traveler, 'Amount') // => 235
Rafi

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สำหรับการสะสมและเพิ่มมูลค่าของรายการวนลูปปัจจุบันไปที่มัน

Yay สำหรับการเขียนโปรแกรมใช้งานได้และ 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


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สำหรับสิ่งนั้น ตัวอย่างด้านล่างโดยที่ value คือแอตทริบิวต์ของคุณที่จะรวมคือ 'value'

_.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');
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.