Power Query 系列 (16) - List.Generate 函數(shù)用法

本篇講解 List.Generate 函數(shù)的用法。這個函數(shù)的功能是用于生成 list,可以是單值,也可以是結(jié)構(gòu)化類型,比較靈活,使用起來有一定難度。

List.Generate(
    initial as function, 
    condition as function, 
    next as function, 
    optional selector as nullable function) as list

4 個參數(shù)都是函數(shù),參數(shù)的含義如下:

  • initial: 通過這個函數(shù)構(gòu)造一個單值或者結(jié)構(gòu)化類型的數(shù)據(jù),單值或者結(jié)構(gòu)化類型的值作為結(jié)果 list 的第一項。第一個函數(shù)作為后面幾個函數(shù)的參數(shù)。
  • condition: 退出循環(huán)的條件。如果函數(shù)返回值為 false,則退出循環(huán);如果函數(shù)的返回值為 true,將當(dāng)前項加入到結(jié)果 list 中。接受第一個函數(shù)為參數(shù)。
  • next:如何構(gòu)造結(jié)果 list 下一項,該參數(shù)接受第一個函數(shù)為參數(shù)。
  • selector:這是唯一一個可選的參數(shù),提供將結(jié)果 list 進行改變的機制。如果不設(shè)置該參數(shù),則第二個參數(shù)返回值為 false 時退出循環(huán),將當(dāng)前的結(jié)果 list 作為函數(shù)的返回值。

有一段 python 語法偽代碼,可以讓我們更好的理解函數(shù)的功能。這段代碼來自:Loops in Power Query M language

def List.Generate(start, condition, next, transform=None):
    results = list()
    item = initial()
    while condition(item) == True:
        results.append(item)
        item = next(item)
    if selector is not None:
        output = list()
        for item in results:
            output.append(selector(item))
    else:
        output = results
    return output

還是覺得比較抽象吧。接下來通過一個例子來幫助我們理解。假設(shè)我們想輸出數(shù)字 1 到 10,用 List.Generate 來實現(xiàn)。在 Power Query 中創(chuàng)建一個空查詢,進入高級編輯器,在高級編輯器中輸入下面的代碼:

let
    Source = List.Generate(
        () => 1,
        (x) => x <= 10,
        (x) => x+1
    )
in
    Source

查詢編輯器顯示的結(jié)果如下:

image

對這個例子解釋一下:

step 1: 通過函數(shù)構(gòu)建一個單值數(shù)據(jù) 1
step 2: 將 1 傳入第二個參數(shù)進行判斷是否小于 10,因為 1 小于 10, 所以 1 被加入結(jié)果 list,即結(jié)果 list 為 {1}
step 3: 將 1 傳入第三個參數(shù),第三個參數(shù)執(zhí)行 1 + 1 運算,結(jié)果變?yōu)?2
step 4: 將 2 傳入第二個參數(shù)進行判斷,執(zhí)行后續(xù)的循環(huán)...

現(xiàn)在將示例修改一下,展示第 4 個參數(shù)的作用。

let
    Source = List.Generate(
        () => 1,
        (x) => x <= 10,
        (x) => x+1,
        (x) => "第 " & Text.From(x) & " 項"
    )
in
    Source

前面 3 個參數(shù)對單值 x 進行判斷和改變,第 4 個參數(shù)構(gòu)建一個字段串作為結(jié)果 list 進行輸出。在查詢編輯器中的顯示如下:

image

List.Generate 的有用之處還是在于結(jié)構(gòu)化類型的構(gòu)建。基于我在參考部分列出的文章示例,我對文中的示例進行了改編,假設(shè)根據(jù)員工在不同 team 的異動記錄,計算出在各 team 的起止日期,結(jié)束日期為在下個 team 的開始日期 - 1:

image

基于剛才對 List.Generate 的介紹,我們直接看 M 語言腳本代碼:

let
    Source = Employees,
    InputData = Table.Sort(
      Source,{{"EmployeeName", Order.Ascending}, {"Date", Order.Ascending}}),

    DoReplace = List.Generate(
        ()=> [Employee="", Team="", StartDate=null,  EndDate=null, Counter=0],  
        each [Counter]<=Table.RowCount(InputData),                              // condition

        each [ 
            Employee=InputData{[Counter]}[EmployeeName], 
            Team=InputData{[Counter]}[Team],  
            StartDate = InputData{[Counter]}[Date],            
            EndDate =
                let 
                    CurrentRowEmployee = InputData{[Counter]}[EmployeeName],
                    NextRowEmployee = InputData{[Counter]+1}[EmployeeName],                  
                    NextRowDate = if NextRowEmployee = null then
                                     null
                                  else if CurrentRowEmployee = NextRowEmployee then
                                        try InputData{[Counter]+1}[Date] otherwise null
                                  else null,
                    MyDate = try Date.AddDays(NextRowDate, -1) otherwise null            
                in  
                    MyDate, 
            Counter=[Counter]+1], 

            // Our list will be a list of these records:
            each [[Employee], [Team], [StartDate], [EndDate]]     // transform
    ), // end of List.Generate
 
    // Convert our list of records into a table
    ConvertedtoTable = Table.FromList(
        DoReplace, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
 
    // Expand it out 
    ExpandedColumn1 = Table.ExpandRecordColumn(
        ConvertedtoTable, 
        "Column1", 
        {"Employee", "Team", "StartDate", "EndDate"}, 
        {"Employee", "Team", "StartDate", "EndDate"}),
 
    // Remove nulls
    FilteredRows = Table.SelectRows(ExpandedColumn1, each ([StartDate] <> null))
in
    FilteredRows
  • 設(shè)置 counter,循環(huán) Table.RowCount(InputData)
  • 第 1 個參數(shù)(initial)構(gòu)造一個空的 record:
()=> [Employee="", Team="", StartDate=null,  EndDate=null, Counter=0]
  • 循環(huán)的時候,每次構(gòu)造一個 record 類型的對象:
[Employee = xxx, Team = xxx, StartDate = xxx, EndDate = xxx, Counter+1]

Counter 作為記數(shù)用,在第 4 個參數(shù)中,只保留有用的部分。

  • 最后通過 Table.FromList 將 list 轉(zhuǎn)換成 table,并對結(jié)構(gòu)化列進行展開。

示例數(shù)據(jù)

github -List.Generate Demo.xlsx

參考

Fun with List.Generate

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容