List.Accumulate เป็นฟังก์ชันสำหรับการวนลูปผ่านแต่ละ element ใน list พร้อมทั้งเก็บสถานะสะสมไว้ตลอดการทำงาน
.
โดยจะเริ่มต้นจากค่าเริ่มต้นที่กำหนด (seed value) และในแต่ละรอบของการวนลูป ฟังก์ชันจะส่งค่าสถานะปัจจุบัน (current state) พร้อมกับ element ปัจจุบันเข้าไปใน accumulator function ที่คุณกำหนดขึ้นมา เพื่อคำนวณและสร้างสถานะใหม่สำหรับรอบถัดไป
.
เมื่อวนลูปครบทุก element แล้ว ฟังก์ชันจะคืนค่าสถานะสุดท้ายกลับมา เหมาะสำหรับการคำนวณยอดสะสม (running totals) การรวมแบบกำหนดเอง (custom aggregations) และการสร้างรูปแบบ For-Next loop pattern ใน M language ครับ
=List.Accumulate(list as list, seed as any, accumulator as function) as any
=List.Accumulate(list as list, seed as any, accumulator as function) as any
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
| list | list | Yes | List ที่ต้องการวนลูปเพื่อประมวลผล แต่ละ element ใน list นี้จะถูกส่งเข้า accumulator function ทีละตัวตามลำดับ | |
| seed | any | Yes | ค่าเริ่มต้น (initial value) สำหรับ accumulated state ซึ่งจะถูกส่งเข้า accumulator function ในรอบแรก สามารถเป็น type ใดก็ได้ (number, text, list, record, null ฯลฯ) ขึ้นอยู่กับการใช้งาน | |
| accumulator | function | Yes | ฟังก์ชันที่กำหนดวิธีการคำนวณ โดยต้องรับ 2 parameters: (1) state – ค่า accumulated value ปัจจุบัน และ (2) current – element ปัจจุบันจาก list จากนั้นคืนค่า new state สำหรับรอบถัดไป รูปแบบ: (state, current) => newState |
ใช้สำหรับสร้างคอลัมน์ยอดสะสม เช่น ยอดขายสะสม, ยอดคงเหลือทางบัญชี, จำนวนสะสม โดยวนลูปผ่านแต่ละแถวและบวกค่าใหม่เข้าไปใน 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 ที่ซับซ้อน
let Numbers = {1, 2, 3, 4, 5}, RunningSum = List.Accumulate( Numbers, 0, (state, current) => state + current ) in RunningSumlet
Numbers = {1, 2, 3, 4, 5},
RunningSum = List.Accumulate(
Numbers,
0,
(state, current) => state + current
)
in
RunningSum
15
let ReplacementPairs = { {"Old", "New"}, {"Bad", "Good"}, {"Slow", "Fast"} }, OriginalText = "This is Old and Bad and Slow", FinalText = List.Accumulate( Replac…let
ReplacementPairs = {
{"Old", "New"},
{"Bad", "Good"},
{"Slow", "Fast"}
},
OriginalText = "This is Old and Bad and Slow",
FinalText = List.Accumulate(
ReplacementPairs,
OriginalText,
(state, current) => Text.Replace(state, current{0}, current{1})
)
in
FinalText
"This is New and Good and Fast"
let OriginalNumbers = {1, 2, 3, 4, 5}, DoubledList = List.Accumulate( OriginalNumbers, {}, (state, current) => state & {current * 2} ) in DoubledListlet
OriginalNumbers = {1, 2, 3, 4, 5},
DoubledList = List.Accumulate(
OriginalNumbers,
{},
(state, current) => state & {current * 2}
)
in
DoubledList
{2, 4, 6, 8, 10}
let ProcessDurations = { #duration(0, 1, 0, 0), // 1 ชั่วโมง #duration(0, 2, 0, 0), // 2 ชั่วโมง #duration(0, 3, 0, 0) // 3 ชั่วโมง }, StartTime = #datetime(202…let
ProcessDurations = {
#duration(0, 1, 0, 0), // 1 ชั่วโมง
#duration(0, 2, 0, 0), // 2 ชั่วโมง
#duration(0, 3, 0, 0) // 3 ชั่วโมง
},
StartTime = #datetime(2025, 12, 17, 9, 0, 0), // 9:00 น.
Timeline = List.Accumulate(
ProcessDurations,
{StartTime},
(timeList, duration) =>
timeList & {List.Last(timeList) + duration}
)
in
Timeline
{
#datetime(2025, 12, 17, 9, 0, 0),
#datetime(2025, 12, 17, 10, 0, 0),
#datetime(2025, 12, 17, 12, 0, 0),
#datetime(2025, 12, 17, 15, 0, 0)
}
let Words = {"Power", "Query", "M", "Language"}, CombinedText = List.Accumulate( Words, null, (state, current) => if state = null then current else state & " -…let
Words = {"Power", "Query", "M", "Language"},
CombinedText = List.Accumulate(
Words,
null,
(state, current) =>
if state = null
then current
else state & " - " & current
)
in
CombinedText
"Power - Query - M - Language"
let IterationCount = 5, IndicesList = {1..IterationCount}, Result = List.Accumulate( IndicesList, "", (state, iteration) => state & "Iteration " & Text.From(ite…let
IterationCount = 5,
IndicesList = {1..IterationCount},
Result = List.Accumulate(
IndicesList,
"",
(state, iteration) =>
state & "Iteration " & Text.From(iteration) & "; "
)
in
Result
"Iteration 1; Iteration 2; Iteration 3; Iteration 4; Iteration 5; "
let Transactions = Table.FromRecords({ [Type = "Income", Amount = 1000], [Type = "Expense", Amount = -300], [Type = "Income", Amount = 500], [Type = "Expense",…let
Transactions = Table.FromRecords({
[Type = "Income", Amount = 1000],
[Type = "Expense", Amount = -300],
[Type = "Income", Amount = 500],
[Type = "Expense", Amount = -200]
}),
AmountList = Transactions[Amount],
StartingBalance = 0,
FinalBalance = List.Accumulate(
AmountList,
StartingBalance,
(balance, transaction) => balance + transaction
)
in
FinalBalance
1000
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 เป็น 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 มีความยืดหยุ่นสูงมากในการนำไปใช้งานกับสถานการณ์ที่หลากหลาย และเป็นเครื่องมือที่ทรงพลังสำหรับนักพัฒนาที่ต้องการควบคุมการประมวลผลข้อมูลอย่างละเอียด