List.Accumulate เป็นฟังก์ชันสำหรับการวนลูปผ่านแต่ละ element ใน list พร้อมทั้งเก็บสถานะสะสมไว้ตลอดการทำงาน
.
โดยจะเริ่มต้นจากค่าเริ่มต้นที่กำหนด (seed value) และในแต่ละรอบของการวนลูป ฟังก์ชันจะส่งค่าสถานะปัจจุบัน (current state) พร้อมกับ element ปัจจุบันเข้าไปใน accumulator function ที่คุณกำหนดขึ้นมา เพื่อคำนวณและสร้างสถานะใหม่สำหรับรอบถัดไป
.
เมื่อวนลูปครบทุก element แล้ว ฟังก์ชันจะคืนค่าสถานะสุดท้ายกลับมา เหมาะสำหรับการคำนวณยอดสะสม (running totals) การรวมแบบกำหนดเอง (custom aggregations) และการสร้างรูปแบบ For-Next loop pattern ใน M language ครับ
| อาร์กิวเมนต์ | ชนิด | คำอธิบาย |
|---|---|---|
| list | list | List ที่ต้องการวนลูปเพื่อประมวลผล แต่ละ element ใน list นี้จะถูกส่งเข้า accumulator function ทีละตัวตามลำดับ |
| seed | any | ค่าเริ่มต้น (initial value) สำหรับ accumulated state ซึ่งจะถูกส่งเข้า accumulator function ในรอบแรก สามารถเป็น type ใดก็ได้ (number, text, list, record, null ฯลฯ) ขึ้นอยู่กับการใช้งาน |
| accumulator | function | ฟังก์ชันที่กำหนดวิธีการคำนวณ โดยต้องรับ 2 parameters: (1) state – ค่า accumulated value ปัจจุบัน และ (2) current – element ปัจจุบันจาก list จากนั้นคืนค่า new state สำหรับรอบถัดไป รูปแบบ: (state, current) => newState |
List.Accumulate เป็น Power Query function ที่ทรงพลังสำหรับการคำนวณแบบวนซ้ำ โดยใช้หลักการของ accumulator pattern (fold/reduce pattern)
.
ที่เจ๋งคือมันจะวนลูปผ่านแต่ละ element ใน list ทีละตัว พร้อมเก็บสถานะสะสมที่ถูกอัพเดทในแต่ละรอบ และส่งผลลัพธ์สุดท้ายกลับมาเป็นค่าเดียว
.
ส่วนตัวผมใช้บ่อยมากเลยครับ โดยเฉพาะตอนต้องทำ running totals หรือสร้าง For-Next loop pattern ใน M language ที่ไม่มี for loop แบบดั้งเดิม 😎
ฟังก์ชันนี้แตกต่างจากฟังก์ชันพื้นฐานทั่วไปอย่าง List.Sum หรือ List.Average ตรงที่คุณสามารถกำหนดตรรกะการคำนวณ (logic) ได้เองผ่านฟังก์ชัน accumulator ที่คุณเขียนขึ้นมา ทำให้สามารถสร้างการรวมแบบกำหนดเอง (custom aggregation) การสะสมแบบมีเงื่อนไข (conditional accumulation) หรือแม้แต่การสร้างลำดับที่มาจากการคำนวณ (derived sequences) ที่มีความซับซ้อนได้ตามต้องการ
List.Accumulate มีบทบาทสำคัญมากในการเขียน M code ระดับขั้นสูง (advanced) โดยเฉพาะอย่างยิ่งเมื่อคุณต้องการทำงานเหล่านี้:
ฟังก์ชันนี้จะคืนค่าประเภท (type) เดียวกับค่าเริ่มต้น (seed value) ที่คุณกำหนด ซึ่งอาจเป็นตัวเลข (number) ข้อความ (text) รายการ (list) เรคคอร์ด (record) หรือประเภทอื่นๆ ก็ได้ ซึ่งทำให้ List.Accumulate มีความยืดหยุ่นสูงมากในการนำไปใช้งานกับสถานการณ์ที่หลากหลาย และเป็นเครื่องมือที่ทรงพลังสำหรับนักพัฒนาที่ต้องการควบคุมการประมวลผลข้อมูลอย่างละเอียด
ใช้สำหรับสร้างคอลัมน์ยอดสะสม เช่น ยอดขายสะสม, ยอดคงเหลือทางบัญชี, จำนวนสะสม โดยวนลูปผ่านแต่ละแถวและบวกค่าใหม่เข้าไปใน state ที่เก็บไว้
M language ไม่มี for loop แบบดั้งเดิม แต่สามารถใช้ List.Accumulate จำลอง for loop ได้โดยการสร้าง list ของ indices และใช้ accumulator function ทำงานซ้ำตามจำนวนรอบที่กำหนด
รวมข้อความจาก list โดยสามารถกำหนดตัวคั่น (separator) แบบ conditional หรือใส่ prefix/suffix ที่แตกต่างกันในแต่ละรอบตามเงื่อนไขที่ซับซ้อน
คำนวณเวลาสิ้นสุดของแต่ละ process โดยเริ่มจาก start time และบวก duration ของแต่ละ process เข้าไปทีละตัว สร้างเป็น timeline ที่แสดงเวลาเริ่มต้นของ process แต่ละตัว
แทนที่หลายคำศัพท์ในข้อความโดยวนลูปผ่าน list ของคู่คำ (old, new) และใช้ Text.Replace ในแต่ละรอบเพื่อแทนที่คำเป้าหมายทีละคำ
สร้าง list ใหม่จาก list เดิมโดยการทำ transformation ที่ต้องอ้างอิงค่าก่อนหน้า เช่น Fibonacci sequence, moving averages หรือ conditional filtering ที่ซับซ้อน
เริ่มจาก seed = 0 แล้วบวก element แต่ละตัวเข้าไปทีละตัว:
• รอบที่ 1: state=0, current=1 → newState=1
• รอบที่ 2: state=1, current=2 → newState=3
• รอบที่ 3: state=3, current=3 → newState=6
• รอบที่ 4: state=6, current=4 → newState=10
• รอบที่ 5: state=10, current=5 → newState=15
ผลลัพธ์สุดท้ายคือ 15 (ผลรวมของ 1+2+3+4+5) ครับ
List.Accumulate วนลูปผ่าน list ของคู่คำศัพท์ {old, new} และใช้ Text.Replace แทนที่คำใน state ทีละคู่:
• state เริ่มต้น = "This is Old and Bad and Slow"
• รอบที่ 1: แทนที่ "Old" → "New"
• รอบที่ 2: แทนที่ "Bad" → "Good"
• รอบที่ 3: แทนที่ "Slow" → "Fast"
current{0} คือ element แรกของคู่ (คำเก่า) และ current{1} คือ element ที่สอง (คำใหม่) ครับ
เริ่มจาก seed = {} (list ว่าง) แล้วนำค่าปัจจุบันคูณ 2 ไปต่อท้าย list ที่สะสมไว้ในแต่ละรอบโดยใช้ & operator:
• state={}, current=1 → newState={2}
• state={2}, current=2 → newState={2,4}
• state={2,4}, current=3 → newState={2,4,6}
ผลลัพธ์คือ list ที่มีค่าเป็น 2 เท่าของ list เดิม (แม้ List.Transform จะทำได้ง่ายกว่า แต่ตัวอย่างนี้แสดงให้เห็นว่า List.Accumulate สามารถสร้าง list ใหม่ได้) 💡
สร้าง timeline ของเวลาเริ่มต้นแต่ละ process โดยเริ่มจาก StartTime และบวก duration ทีละตัว:
• seed = {9:00} (list ที่มี StartTime)
• รอบที่ 1: เอา 9:00 + 1 ชม. = 10:00 → {9:00, 10:00}
• รอบที่ 2: เอา 10:00 + 2 ชม. = 12:00 → {9:00, 10:00, 12:00}
• รอบที่ 3: เอา 12:00 + 3 ชม. = 15:00 → {9:00, 10:00, 12:00, 15:00}
List.Last(timeList) จะดึงเวลาล่าสุดจาก list และบวก duration เพื่อหาเวลาเริ่มต้นของ process ถัดไป เหมาะสำหรับการวางแผน scheduling หรือ Gantt chart ครับ 😎
รวมข้อความจาก list โดยใส่ตัวคั่น " – " ระหว่างคำ โดยใช้ conditional logic:
• seed = null (ไม่มีข้อความเริ่มต้น)
• รอบที่ 1: state=null, current="Power" → newState="Power"
• รอบที่ 2: state="Power", current="Query" → newState="Power – Query"
• รอบที่ 3: state="Power – Query", current="M" → newState="Power – Query – M"
การใช้ if condition ช่วยป้องกันไม่ให้มีตัวคั่นนำหน้าคำแรก (ซึ่ง Text.Combine ก็ทำได้เช่นกัน แต่ตัวอย่างนี้แสดงความยืดหยุ่นของ List.Accumulate ที่สามารถใส่ logic ซับซ้อนได้) ครับ
จำลอง For-Next loop ใน M language โดยสร้าง list ของ indices {1..5} และใช้ List.Accumulate วนลูป 5 รอบ:
• IndicesList = {1, 2, 3, 4, 5}
• state เริ่มต้น = "" (empty text)
• แต่ละรอบจะเพิ่มข้อความ "Iteration N; " เข้าไป
เทคนิคนี้มีประโยชน์เมื่อต้องการทำงานซ้ำๆ จำนวนครั้งที่กำหนด โดยไม่สนใจค่าใน list (ใช้แค่จำนวน elements เป็นตัวกำหนดรอบ loop) ครับ
คำนวณยอดเงินคงเหลือสุดท้ายจาก list ของรายการรับ-จ่าย:
• AmountList = {1000, -300, 500, -200}
• balance เริ่มต้น = 0
• รอบที่ 1: 0 + 1000 = 1000
• รอบที่ 2: 1000 + (-300) = 700
• รอบที่ 3: 700 + 500 = 1200
• รอบที่ 4: 1200 + (-200) = 1000
หมายเหตุ: หาก balance เป็น negative จะแสดงว่าเงินไม่พอ ตัวอย่างนี้แสดงการใช้ List.Accumulate กับ real-world scenario ในการทำบัญชีครับ 💡
List.Sum และ List.Average เป็นฟังก์ชัน specific สำหรับการรวมตัวเลขเท่านั้น
ในขณะที่ List.Accumulate เป็นฟังก์ชัน generic ที่ให้คุณกำหนด custom logic ผ่าน accumulator function ได้เอง ทำให้สามารถทำงานที่ซับซ้อนกว่าได้ เช่น รวมข้อความ, สร้าง list ใหม่, คำนวณแบบมีเงื่อนไข, หรือสร้าง timeline จาก duration values ครับ
accumulator function ต้องรับ 2 parameters: (1) state – ค่า accumulated value ปัจจุบัน (เริ่มจาก seed value) และ (2) current – element ปัจจุบันจาก list ที่กำลังประมวลผล
ฟังก์ชันต้องคืนค่า new state สำหรับรอบถัดไป รูปแบบ: (state, current) => expression ตัวอย่าง: (sum, num) => sum + num หรือ (text, word) => text & " " & word ครับ
ได้ แต่ List.Accumulate คืนค่าเป็น single value (ยอดสุดท้าย) ไม่ใช่ list ของยอดสะสมแต่ละรอบ
หากต้องการ running totals column ควรใช้ List.Generate แทน ซึ่งสามารถสร้าง list ของ intermediate results ได้ หรือใช้ Table.AddColumn ร่วมกับ List.FirstN และ List.Sum เพื่อคำนวณ cumulative sum ของแต่ละแถวครับ
ถ้า list ว่าง (empty list {}) List.Accumulate จะคืนค่า seed value ทันที โดยไม่เรียก accumulator function เลย
เพราะไม่มี element ให้ประมวลผล ตัวอย่าง: List.Accumulate({}, 100, (s, c) => s + c) จะคืนค่า 100 ครับ
ไม่จำเป็น แต่แนะนำให้ seed เป็น type เดียวกับผลลัพธ์ที่คาดหวัง เพราะ seed คือค่าเริ่มต้นของ state และ accumulator function ต้องคืนค่า new state ที่เป็น type เดียวกันในทุกรอบ
ตัวอย่าง: หากต้องการผลลัพธ์เป็น list ก็ใช้ seed = {} (empty list), หากต้องการ number ก็ใช้ seed = 0 หรือ null หากยอมรับค่า null ได้ครับ
List.Accumulate คืนค่า single accumulated value (final result) ในขณะที่ List.Generate สร้าง list ของ intermediate results ทุกรอบ
ใช้ List.Accumulate เมื่อต้องการแค่ผลลัพธ์สุดท้าย (เช่น sum, final balance) และใช้ List.Generate เมื่อต้องการ list ของยอดสะสมทุกรอบ (เช่น running totals column) นอกจากนี้ List.Accumulate มี memory efficiency ดีกว่าเพราะเก็บแค่ state เดียว ไม่ใช่ทั้ง list ครับ 😎
สร้าง list ของ indices (เช่น {1..10} สำหรับ 10 รอบ) และใช้ List.Accumulate วนลูปผ่าน list นี้
โดย current parameter จะเป็นหมายเลขรอบ (iteration number) และ state จะเก็บผลลัพธ์ที่สะสม ตัวอย่าง: List.Accumulate({1..5}, 0, (state, i) => state + i) จะบวกเลข 1 ถึง 5 เทคนิคนี้มีประโยชน์เมื่อต้องการ repeat operation จำนวนครั้งที่กำหนดครับ
ไม่ได้โดยตรง List.Accumulate จะวนลูปผ่านทุก element ใน list เสมอ ไม่มีวิธี break หรือ early exit แบบ traditional loop
หากต้องการ conditional stopping ต้องใช้ List.Generate แทน ซึ่งสามารถกำหนด condition function เพื่อหยุดการ generate elements ได้ หรือใช้ List.FirstN กับ List.PositionOf เพื่อตัด list ก่อนส่งเข้า List.Accumulate ครับ
ฟังก์ชันที่ผู้เขียนโยงไว้กับ List.Accumulate จัดกลุ่มตามหมวด · ชี้ที่ชื่อใดชื่อหนึ่ง แล้วฟังก์ชันที่ใช้คู่กันจะมีจุดทอง
List.Average คำนวณค่าเฉลี่ยของตัวเลข วันที่ เวลา หรือระยะเวลาในรายการ ฟังก์ชันนี้เป็นตัวช่วยแรกที่ผมใช้ทุกครั้งที่ต้องการวิเคราะห์ค่าเฉลี่ยจากชุดข้อมูลใดๆ
รวมหลาย List เข้าด้วยกันเป็น List เดียว (Concatenate)
List.ConformToPageReader ใช้ภายในเพื่อแปลง List ของ Record เป็น Table ตาม Schema ที่กำหนด เหมาะสำหรับโครงสร้างข้อมูลที่ Page Reader ต้องการ
List.Count ใช้นับจำนวนสมาชิกทั้งหมดในลิสต์ (รวม null) ส่งคืนค่าตัวเลข ถ้าต้องการนับเฉพาะที่ไม่ว่างให้ใช้ List.NonNullCount แทน
List.FirstN จะคืน List ใหม่ที่มีสมาชิก N ตัวแรกจาก List ต้นฉบับ สามารถระบุจำนวน N ด้วยตัวเลข หรือกำหนดเป็นฟังก์ชันเงื่อนไขเพื่อให้ดึงสมาชิกที่ตรงเงื่อนไขจากจุดเริ่มต้นไปเรื่อยๆ จนกว่าเงื่อนไขจะไม่เป็นจริง
List.Generate เป็น generator function ที่สร้าง list โดยเริ่มจากค่าเริ่มต้น (initial) วนลูปตรวจสอบเงื่อนไข (condition) สร้างค่าถัดไป (next) และ transform ผลลัพธ์ด้วย selector (optional) ทำงานคล้าย while loop ในการเขียนโปรแกรม เหมาะสำหรับสร้างลำดับวันที่, pagination API, การคำนวณทบต้น และ pattern ซับซ้อนที่ List.Numbers ทำไม่ได้
List.Last คืนค่าสมาชิกตัวสุดท้ายของ List หรือค่า Default ถ้า List ว่าง
List.PositionOf จะคืนค่า Index แรกสุด (0-based) ของ value ที่พบใน list ถ้าไม่พบจะคืนค่า -1 สามารถระบุ occurrence เพื่อหาตำแหน่งของตัวที่ซ้ำกัน และใช้ comparer เพื่อกำหนดวิธีการเปรียบเทียบ (Case Sensitive/Insensitive)
List.Sum รวมค่าตัวเลขทั้งหมดในรายการ โดยละเว้นค่า null โดยอัตโนมัติ ฟังก์ชันนี้เป็นพื้นฐานสำหรับการคำนวณสรุป การจำสรุป และการวิเคราะห์ข้อมูลในการแปลงข้อมูล
List.Transform ใช้สำหรับสร้างลิสต์ใหม่โดยนำฟังก์ชันไปประมวลผลแต่ละตัวในลิสต์เดิม
Comments
อีเมลของคุณจะไม่ถูกเผยแพร่