Skip to main content
0
JavaScriptProgramming Language

Reduce ใน javascript ไม่ได้หาได้แค่ผลรวมนะ

เพื่อน ๆ หรือใครหลายๆ คนคงน่าจะคุ้นหน้าคุ้นตา Array Method กันมาบ้าง ซึ่งเป็นส่วนหนึ่งของ Javascript ที่ชื่อว่า Reduce  แต่สำหรับใครที่ยังไม่รู้ว่ามันคืออะไรก็สามารถอ่านใน ทความนี้ ได้เลยครับ

ถึงแม้จะเป็น method ที่ชวนงงเล็กน้อย แต่สำหรับใครหลายคนในการใช้งานตอนแรก Reduce ก็เป็น method ที่สามารถนำไปประยุกต์ใช้ได้หลากหลายมากตัวหนึ่ง นอกเหนือจากการหาผลรวมแล้วในบทความนี้เราจะมาใช้ Reduce กันเพิ่มเติมกับ ทริคการใช้งาน .reduce ใน javascript จะมีอะไรกันบ้าง ไปดูกัน

เขียนโดย
Thanawat Udchachon
Internship @ borntoDev

เรามาดูการทำงานของ Reduce กันก่อน

Reduce เป็น Array Method ที่จะมีการ return ค่าออกมาเป็นค่าเดียว ต่างจาก Map และ Filter ที่จะ return ออกมาเป็น Array ใหม่อีกตัว

Reduce จะมี 4 arguments ดังนี้

  • ค่าก่อนหน้า

  • ค่าปัจจุบัน

  • ตำแหน่งปัจจุบัน (index)

  • Array ที่เราเรียกใช้

จะเห็นได้ว่าจะมี argument ที่เพิ่มมาจาก Map และ Filter คือ ค่าก่อนหน้า

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

let price = [42, 32, 20]

let sum = price.reduce(function (prev, curr) {
    return prev + curr;
}, 0);

console.log(sum); //94

เชื่อว่านี่คงเห็นโค้ดที่หลายคนเคยเห็น ว่าแต่มันทำงานยังไง

ขั้นแรก sum ของเราจะมีค่าเริ่มต้นเป็น 0 ซึ่งเป็นค่าที่เราได้ใส่เอาไว้

เมื่อมาถึง element แรกใน array เราก็บอกว่า ให้ค่าก่อนหน้าซึ่งในที่นี้คือ 0 ที่มาจากค่าเริ่มต้น บวกกับค่า element ปัจจุบันซึ่งคือ 42 แล้วเราก็ return ผลรวมนั้นออกมา ซึ่งเป็นการเซ็ตค่า prev นั่นเอง ทำให้ตอนนี้ prev มีค่า 42

ต่อมาเมื่อมาถึง element ตัวถัดมาใน array ก็จะทำแบบเดิมคือ return ผลรวมของ prev ซึ่งตอนนี้คือ 42 บวกกับ element ปัจจุบันซึ่งคือ 32 เราก็จะได้ค่าใหม่ของ prev เป็น 74

และเมื่อมาถึง element ตัวสุดท้ายใน array ก็จะทำแบบเดิมเช่นกันคือ return 74 บวก 20 และเมื่อเรา console.log(sum) ออกมาเราก็จะได้ 94 ออกมานั่นเองครับ

หรือเราสามารถเขียนการทำงานเดียวกันนี้ให้สั้นกว่าเดิมโดยใช้ Arrow function ก็สามารถเขียนได้ดังนี้ครับ

let price = [42, 32, 20]

let sum = price.reduce((prev, curr) => prev + curr, 0);

console.log(sum); //94

หลายคนอาจจะสงสัยว่าถ้าเราไม่ได้กำหนดค่าเริ่มต้นจะเป็นยังไง ถ้าเราไม่ได้กำหนดค่าเริ่มต้น ค่าก่อนหน้าจะเป็น element แรกใน array ครับ ซึ่งในที่นี้ก็คือ 42 ซึ่งถ้าถามว่ามันจะได้ผลลัพธ์ที่ต่างกันไหม สำหรับการบวกเลขแบบนี้จะได้ผลลัพธ์ที่ไม่ต่างกันครับ แต่สำหรับการทำงานแบบอื่น ๆ ก็สามารถทำให้ผลลัพธ์ต่างออกไปได้และอาจจะทำให้ผลที่ออกมาคลาดเคลื่อนไปจากที่เราต้องการได้ครับ ดังนั้นก็ควรระวังเอาไว้ด้วยนะครับ

นอกจาก Reduce ปกติที่จะเป็นการไล่ array จากซ้ายไปขวาแล้ว ยังมีอีก method ที่ชื่อว่า reduceRight ที่จะทำงานแบบเดียวกันกับ Reduce ปกติเลย เพียงแค่จะไล่ array จากขวาไปซ้ายครับและนั่นก็คือการทำงานของ Reduce ครับ

จะเห็นได้ว่า Reduce ไม่ได้ทำได้เพียงการรวมค่าแต่อย่างใด มันสามารถที่จะ return ค่าใดก็ได้ที่ผ่านมาจากการทำงานกับ array ที่เราเรียกใช้ ขึ้นอยู่กับเราว่าเราต้องการอะไร ซึ่งมันก็คือการลดจาก array มาเป็นค่าเดียวตามชื่อนั่นเองครับ

ว่าแล้วเราก็ลองไปดูการใช้งาน Reduce แบบอื่น ๆ บ้างกันเลยดีกว่า

 

หาค่า min max

เรามาเริ่มกันกับอะไรง่าย ๆ กันก่อนกับการหา element ที่มีค่ามากที่สุดและน้อยที่สุด

let price = [42, 32, 20]

let max = price.reduce(function (prev, curr) {
return curr > prev ? curr : prev;
});

console.log(max); //42

จากโค้ดจะเป็นหารหาค่าที่มากที่สุด การทำงานนี้ก็จะคล้ายกับการทำงานในหัวข้อก่อนหน้านี้ เพียงแค่เปลี่ยนจากการ return ผลรวม เป็นการตรวจสอบว่า element ในปัจจุบันมีค่ามากกว่าตัวก่อนหน้าหรือไม่ ถ้ามากกว่าก็ให้ return element ปัจจุบัน แต่ถ้าไม่ก็ให้ return ค่าก่อนหน้า แล้วทำแบบนี้ไปเรื่อย ๆ จนครบ element ทุกตัวครับ

และสำหรับการหาค่าที่น้อยที่สุดก็จะเหมือนกันการหาค่าที่มากที่สุดเลยครับ แค่เปลี่ยนจากสัญลักษณ์จากมากกว่า (>) เป็นน้อยกว่า (<) เราก็จะได้โค้ดออกมาดังนี้ครับ

let price = [42, 32, 20]

let min = price.reduce(function (prev, curr) {
return curr < prev ? curr : prev;
});

console.log(min); //20

จะเห็นได้จากโค้ดทั้งสองว่า ไม่ได้มีการกำหนดค่าเริ่มต้น เป็นเพราะว่าเราต้องการเปรียบเทียบค่าใน array ที่มีอยู่แล้วครับ จึงไม่จำเป็นที่ต้องกำหนดค่าเริ่มต้น

 

สามารถใช้แทน Map และ Filter ได้นะ

เรื่องหนึ่งที่น่าสนใจของ Reduce คือมันสามารถใช้แทน Map และ Filter ได้! โดยการกำหนดค่าเริ่มต้นเป็น Array เปล่า เป็นยังไง ไปดูกัน

let map = price.reduce(function (prev, curr) {
return [...prev, curr];
}, []);

console.log(map); //[ 42, 32, 20 ]

จากโค้ดก็คือ เมื่อผ่านเข้าไปใน element แต่ละตัวก็ให้ทำการ return ข้อมูลที่อยู่ใน array เดิมทั้งหมด (สำหรับ element แรก ขั้นตอนนี้ prev จะยังไม่มี เพราะยังเป็น array เปล่าอยู่) แล้วต่อด้วย element ในปัจจุบัน แล้วก็ทำอย่างนี้ไปเรื่อย ๆ จนครบทุก element โดยเราสามารถให้เกิดกระบวนการบางอย่างให้กับแต่ละ element ได้เช่นเดียวกับ Map เลย

แต่สำหรับในส่วนของ Performance นั้น Map สามารถทำงานได้เร็วกว่าเล็กน้อยครับ

และสำหรับ Filter ก็คล้ายกับ Map เลย เพียงแค่เพิ่ม if เข้าไป

let filter = price.reduce(function (prev, curr) {
    if (curr > 20) {
        return [...prev, curr]; //ใช้ prev.push(curr) แทนได้เพราะไม่ใช่ return สุดท้าย
}
    return [...prev];
}, []);

console.log(filter); //[ 42, 32 ]

ทำงานก็คือ เมื่อผ่านเข้าไปใน element แต่ละตัวก็จะทำการตรวจสอบว่า element ดังกล่าวเข้าเงื่อนไขที่ตั้งไว้หรือไม่ ถ้าเข้าก็จะ return ข้อมูลที่อยู่ใน array เดิมทั้งหมด (สำหรับ element แรก ขั้นตอนนี้ prev จะยังไม่มี เพราะยังเป็น array เปล่าอยู่) แล้วต่อด้วย element ในปัจจุบัน แต่ถ้าไม่ก็จะ return เพียงแค่ ข้อมูลที่อยู่ใน array เดิมทั้งหมด ซึ่งก็คือไม่ได้มีการเพิ่มอะไรเข้าไปนั่นเอง

และสำหรับในส่วนของ Performance นั้น Filter ก็สามารถทำงานได้เร็วกว่าเล็กน้อยครับ

แต่ยังไม่จบเพียงเท่านี้!

เพราะเราสามารถให้ reduce ทำงานเหมือนกับการนำ Filter และ Map มา chain ต่อกันได้ด้วยเช่นกัน!

ในที่นี้จะเริ่มจากข้อมูลของเรากันก่อนที่จะเป็นข้อมูลชื่อสินค้าและราคาดังนี้

let receipt = [

   { name: "sausage", price: 42 },

   { name: "coffee", price: 32 },

   { name: "orange", price: 20 }
 
]

แล้วเราต้องการรู้ชื่อรายการอาหารที่มีราคามากกว่า 20 ถ้าเขียนโดยใช้ Map ที่ chain กับ Filter ก็จะได้ดังนี้

let realFilterMap = receipts.filter(function (receipt) {
    return receipt.price > 20;
}).map(function (receipt) {
   return receipt.name;
});

console.log(realFilterMap); //[ 'sausage', 'coffee' ]

โดยเราสามารถเขียนการทำงานเดียวกันนี้ด้วย Reduce ได้ดังนี้

let filterMap = receipts.reduce(function (prev, curr) {
    if (curr.price > 20) {
        prev.push(curr.name)
    }
    return prev
}, []);

console.log(filterMap); //[ 'sausage', 'coffee' ]

มาถึงเรื่องที่ทุกคนรอคอย! (รึป่าว) Performance นั่นเอง!! ผลก็คือ ทั้งสองวิธีใช้เวลาในการทำงานที่ใกล้เคียงกันมากครับ

มีค่าเริ่มต้นได้มากกว่า 1 ตัว !?

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

let receipt = [

    { name: "sausage", price: 42 },

    { name: "coffee", price: 32 },

    { name: "orange", price: 20 }

]

โดยเราต้องการแบ่งกลุ่มรายการอาหารราคาถูกและราคาแพง โดยอาหารที่มีราคามากกว่า 20 จะอยู่ในกลุ่มอาหารแพง

let multipleInit = receipts.reduce(function (prev, curr) {
    if (curr.price > 20) {
        prev.expensive.push(curr);
    } else {
        prev.cheap.push(curr);
    }
    return prev;
}, {
    expensive: [], //Array สำหรับเก็บรายการอาหารที่มีราคาสูงกว่า 20
    cheap: [], //Array สำหรับเก็บรายการอาหารที่มีราคาต่ำกว่า 20 หรือต่ำกว่า
});

console.log(multipleInit)
// {
// expensive: [ { name: 'sausage', price: 42 }, { name: 'coffee', price: 32 } ],
// cheap: [ { name: 'orange', price: 20 } ]
// }

การทำงานก็คือ เมื่อผ่านเข้าไปใน element แต่ละตัวก็จะทำการตรวจสอบว่า element ดังกล่าวนั้นมีราคามากกว่า 20 หรือไม่ ถ้าใช่ให้ push element นั้นเข้าไปใน array expensive ถ้าไม่ก็ให้ push element นั้นเข้าไปใน array cheap แทน แล้วสุดท้ายก็ return ค่า prev ที่มีการเพิ่มข้อมูลเพิ่มเติมเข้าไปแล้ว แล้วก้ทำอย่างนี้จนครบทุก element เราก็จะได้การแบ่งกลุ่มออกมานั่นเอง

มีชื่อต้องมีราคา

ในบางโปรแกรมของเราที่ต้องมีการกรอกข้อมูลสองอย่างขึ้นไปที่เกี่ยวข้องกัน บางครั้งเราก็ต้องมีการตรวจสอบว่าผู้ใช้งานได้ใส่ข้อมูลครบหรือไม่ หรือถ้าไม่กรอก ก็ไม่ควรกรอกทั้งสองช่อง อธิบายอาจจะงง งั้นเราไปดูตัวอย่างกันเลยดีกว่า และเช่นเดิม เราก็จะใช้ข้อมูลรายการอาหารเหมือนเดิมแต่ดัดแปลงเล็กน้อย

let receipt = [

    { name: "sausage", price: 42 },

    { name: "coffee", price: 32 },

    { name: "", price: "" }

]

โดยเราต้องการตรวจสอบว่าในแต่ละ Object มีข้อมูลครบทั้ง 2 ช่องหรือไม่ หรือถ้าไม่มีก็ไม่ควรมีทั้ง 2 ช่อง

let fullfill = receipts.reduce((prev, curr) => ((curr.name !== '' && 
curr.price !== '') || (curr.name === '' && curr.price === '') ? prev : prev + 1), 0)

console.log(fullfill)

การทำงานก็คือ เมื่อผ่านเข้าไปใน element แต่ละตัวก็จะทำการตรวจสอบว่า element ดังกล่าวนั้นมีข้อมูลทั้งสองช่อง หรือ ไม่มีทั้งสองช่องหรือไม่ ถ้าใช่จะ return ออกมาเป็น 0 และถ้าไม่ หรือก็คือมีข้อมูลแค่ name หรือ price ตามข้อมูลตัวอย่างถัดจากนี้ ก็จะ return ออกมาเป็น 1 หรือมากกว่า แล้วเราก็สามารถเอาตัวเลขเหล่านี้ไปใช้ต่อในขั้นตอนอื่น ๆ ในโค้ดของเราได้ครับ

let receipt = [

    { name: "sausage", price: 42 },

    { name: "coffee", price: "" },

    { name: "", price: "" }

]

สรุปสุดท้ายสิ่งที่ผู้อ่านจะได้รับ

เป็นยังไงกันบ้างครับสำหรับการใช้งาน Reduce ที่นอกเหนือจากการหาผลรวม เรียกว่าเป็น Array Method ตัวหนึ่งที่อาจจะงงใช้ได้สำหรับการใช้งานในตอนแรก แต่ในขณะเดียวกันก็เป็น method ที่สามารถประยุกต์ใช้ได้หลากหลายเช่นกัน ในบทความนี้เป็นเพียงตัวอย่างที่ยกมานำเสนอเพียงบางส่วนเท่านั้น และแน่นอนว่าเรายังสามารถประยุกต์ใช้มันได้อีกมากมายเลย ไม่แน่ว่าโปรแกรมที่เพื่อน ๆ กำลังโค้ดอยู่ก็อาจจะสามารถนำ Reduce เข้ามาช่วยได้ก็ได้นะ

 

 

อ้างอิงจาก

  1. How to Use Map, Filter, and Reduce in JavaScript, สืบค้นเมื่อ 22 เมษายน 2565 จาก: https://code.tutsplus.com/tutorials/how-to-use-map-filter-reduce-in-javascript–cms-26209
  2. JavaScript reduce behavior with/without an initial value, สืบค้นเมื่อ 2 พฤษภาคม 2565 จาก: https://stackoverflow.com/questions/50826718/javascript-reduce-behavior-with-without-an-initial-value
  3. Array.prototype.reduce(), สืบค้นเมื่อ 2 พฤษภาคม 2565 จาก: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
  4. 13 ท่า Reduce Array สยอง, สืบค้นเมื่อ 2 พฤษภาคม 2565 จาก: https://medium.com/dev-it/13-ท่า-reduce-array-สยอง-a619e65b1984
  5. .map(), .filter() และ .reduce() สามทหารเสือในการจัดการกับ Array[…], สืบค้นเมื่อ 2 พฤษภาคม 2565 จาก: https://medium.com/@rennerwin/map-filter-reduce-สามทหารเสือในการจัดการกับ-array-49b2cd966e79
  6. JavaScript, Tricks with Reduce, สืบค้นเมื่อ 2 พฤษภาคม 2565 จาก: https://stephengalvan.medium.com/javascript-tricks-with-reduce-9c3a6f5d6168
  7. [JavaScript] Array .reduce() ใช้งานยังไง?, สืบค้นเมื่อ 2 พฤษภาคม 2565 จาก: https://www.kotenarok.com/2020/12/javascript-array-reduce.html

หากคุณสนใจพัฒนา สตาร์ทอัพ แอปพลิเคชัน
และ เทคโนโลยีของตัวเอง ?

อย่ารอช้า ! เรียนรู้ทักษะด้านดิจิทัลเพื่ออัพเกรดความสามารถของคุณ
เริ่มตั้งแต่พื้นฐาน พร้อมปฏิบัติจริงในรูปแบบหลักสูตรออนไลน์วันนี้

BorntoDev

Author BorntoDev

BorntoDev Co., Ltd.

More posts by BorntoDev

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

ตั้งค่าความเป็นส่วนตัว

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

ยอมรับทั้งหมด
จัดการความเป็นส่วนตัว
  • คุกกี้ที่จำเป็น
    เปิดใช้งานตลอด

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

  • คุกกี้สำหรับการติดตามทางการตลาด

    ประเภทของคุกกี้ที่มีความจำเป็นในการใช้งานเพื่อการวิเคราะห์ และ นำเสนอโปรโมชัน สินค้า รวมถึงหลักสูตรฟรี และ สิทธิพิเศษต่าง ๆ คุณสามารถเลือกปิดคุกกี้ประเภทนี้ได้โดยไม่ส่งผลต่อการทำงานหลัก เว้นแต่การนำเสนอโปรโมชันที่อาจไม่ตรงกับความต้องการ
    รายละเอียดคุกกี้

บันทึกการตั้งค่า