EP029 List.Generate:财务BP如何用循环把「表头+多人」拆成工资条、按步长筛达标产品

更新于 2026-07 · 作者:汪斌(老汪),制造业财务实战 · Excel BI(Power Query / Power Pivot / CUBE)
系列:Excel BI 财务建模实战课讲义(第二阶段)· Power Query / M 语言 · List
SEO focus:List.Generate
SEO 关键词:List.Generate,Power Query,M语言,循环构建表,Table.ToRecords,List.Combine,List.Range,工资条,财务BP,Excel BI

直接答案(TL;DR)

List.Generate 用「初始值 → 条件 → 下一步 →(可选)取值」循环造列表。财务BP用它两件事:把「一行表头 + 多行人员」拆成一人一页工资条;把「每产品连着 N 行工序」按步长切片,再筛平均达标的产品。


学完 EP029 能独立完成哪三件事?

  1. 写倒序序列(如 10→1),并知道正序优先 {1..10},别硬 Generate。
  2. 用 Generate 第四参数把「下标」映射成「表头记录 + 当前人记录」,再 Combine 成工资条。
  3. 用 Generate 步长 3 + List.Range 切三工序块,再用 List.Select + List.Average 筛达标产品。

List.Generate 四个参数分别干什么?

参数 角色 例子(倒序 10→1)
1 初始值 循环从哪开始 () => 10
2 条件 什么时候继续 each _ > 0
3 下一步 状态怎么变 each _ - 1
4 取值(可选) 这一步真正往列表里塞什么 省略则塞状态本身

空白查询可直接验证:

List.Generate(
    () => 10,
    each _ > 0,
    each _ - 1
)

期望返回:{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}

正序 1→10 也能 Generate,但更简单:

{1..10}

原则:能用字面量 / List.Numbers 解决的,不要上 Generate;Generate 留给「步长不规则、或每一步要组装复杂结构」的场景。


案例一:华凯精工 · 车间计件工资条怎么拆?

场景:人事把「表头一行 + 多人一行」宽表导出给财务BP。车间主任要的是一人一页确认条:每页先看字段名,再看该人数字。

教学源表(导入后先「使用第一行作为标题」的反操作——降级标题,让表头也变成数据行):

Column1 Column2 Column3 Column4
姓名 基本工资 计件工资 补贴
张工 4200 1800 300
李工 4000 2100 200
王工 3800 1500 250

步骤 0:导入,删「更改的类型」(可选)

方便公式锚定步骤名。

步骤 1:降标题 + 行转记录

行转记录 = Table.ToRecords(Table.DemoteHeaders(源))

此时 行转记录{0} = 表头那一行记录;行转记录{1} = 张工;{2} = 李工;{3} = 王工。共 4 条。

步骤 2:Generate 下标,跳过第 0 行

只要「人」的下标:1、2、3。

人员下标 = List.Generate(
    () => 1,
    each _ < List.Count(行转记录),
    each _ + 1
)

期望:{1, 2, 3}

步骤 3:第四参数——每一步拼「表头 + 当前人」

工资条块 = List.Generate(
    () => 1,
    each _ < List.Count(行转记录),
    each _ + 1,
    each { 行转记录{0}, 行转记录{_} }
)

每一步得到一个含两条记录的列表{表头, 张工} / {表头, 李工} / {表头, 王工}

若要页间空行,可写成:

each { 行转记录{0}, 行转记录{_}, null }

(预览可能报错显示,加载到工作表后多为空行,属预期。)

步骤 4:合并 → 记录转表 → 提升标题

合并 = List.Combine(工资条块),
成表 = Table.FromRecords(List.Select(合并, each _ <> null)),
结果 = Table.PromoteHeaders(成表)

得到:张工页(表头+数据)、李工页、王工页依次堆叠——这就是「工资条」结构。


案例二:产品 × 三道工序,如何筛平均产量达标?

场景:MES 导出一维长表,每个产品固定三行(下料 / 机加 / 装配)。经营会只要「三工序平均产量 ≥ 85」的产品清单。

产品编码 工序 产量
P-A 下料 98
P-A 机加 95
P-A 装配 90
P-B 下料 70
P-B 机加 72
P-B 装配 68
P-C 下料 88
P-C 机加 90
P-C 装配 86
P-D 下料 92
P-D 机加 80
P-D 装配 78

手算平均:P-A 94.33、P-B 70、P-C 88、P-D 83.33 → 达标应剩 P-A、P-C

步骤 1:行转记录(本案例不降标题)

行转记录 = Table.ToRecords(源)

12 条记录;下标 0–2 = P-A,3–5 = P-B,6–8 = P-C,9–11 = P-D。

步骤 2:Generate 步长 3 + Range 切片

按产品切块 = List.Generate(
    () => 0,
    each _ < List.Count(行转记录),
    each _ + 3,
    each List.Range(行转记录, _, 3)
)

得到 4 个「各含 3 条记录」的子列表。

步骤 3:Select 平均产量 ≥ 85

达标块 = List.Select(
    按产品切块,
    each List.Average({ _{0}[产量], _{1}[产量], _{2}[产量] }) >= 85
)

步骤 4:Combine → FromRecords

合并 = List.Combine(达标块),
结果 = Table.FromRecords(合并)

期望:只剩 P-A、P-C 共 6 行。改源表产量后刷新,名单跟着变——这是 Generate 切片的价值。

实务提醒:若只是「先算平均再筛选」,透视表 / 分组更直观。本课重点是练 Generate + Range 步长思维;上会报表选更简单的路即可。


和 List.Numbers / {1..n} / List.Range 怎么选?

需求 更合适
连续正序整数 {1..n} / List.Numbers
从已有列表按起点+长度取一段 List.Range(EP027)
倒序、不规则步长、每步组装结构 List.Generate(本课)
按条件留下元素 List.Select(EP028)

操作自检:案例与公式能否一一对上?

自检结论(2026-07-23):PASS。

演示 输入 期望输出
倒序 Generate 10,_ > 0_ - 1 {10..1}
人员下标 Count=4,从 1 起 {1,2,3}
P-A 平均产量 98,95,90 94.33 ≥85 → 保留
P-B 平均产量 70,72,68 70 → 剔除
P-C 平均产量 88,90,86 88 ≥85 → 保留
P-D 平均产量 92,80,78 83.33 → 剔除
达标结果产品 P-A、P-C

上会前财务BP要自检哪三项?

  1. Generate 的「停止条件」是否用 < List.Count(...),避免越界或少取一行。
  2. 步长是否等于「每个对象固定行数」(三工序 → +3;若有四行成本要素 → +4)。
  3. 工资条场景是否永远钉住 行转记录{0} 当表头,人员下标从 1 起。

读者常问 AI 的问题有哪些?(FAQ)

List.Generate 和 for 循环是一回事吗?

语义接近「带状态的循环」,但是函数式:你声明初始/条件/下一步,引擎生成列表,没有过程式 for 写法。

第四参数什么时候必须写?

当你循环的「状态」(如下标 1,2,3)本身不是最终要的值,而要映射成记录块、Range 切片时——本课两个案例都必须写。

为什么工资条一定要降标题?

因为表头也要变成「可被下标引用的记录」。不降标题时,字段名在列名里,拼不出「表头行 + 数据行」成对结构。

空行用 null 后预览报错正常吗?

常见。加载到工作表后多为空白行。正式上会前可在 FromRecords 前 List.Select(..., each _ <> null) 或接受空行展示。

和 EP027 List.Range、EP028 Select/Skip 什么关系?

Generate 负责「造下标 / 造块」;Range 负责「按起点取一段」;Select/Skip 负责筛和丢。本课把三者串成一条生产链。


本课公式速查表在哪里?

// 倒序
List.Generate(() => 10, each _ > 0, each _ - 1)

// 工资条:下标 + 表头配对
List.Generate(
    () => 1,
    each _ < List.Count(行转记录),
    each _ + 1,
    each { 行转记录{0}, 行转记录{_} }
)

// 三工序切块
List.Generate(
    () => 0,
    each _ < List.Count(行转记录),
    each _ + 3,
    each List.Range(行转记录, _, 3)
)

// 筛平均达标
List.Select(按产品切块, each List.Average({_{0}[产量], _{1}[产量], _{2}[产量]}) >= 85)

文末

我是汪斌(老汪),长期做制造业成本、业财与 Excel BI 建模。本篇是陪跑课讲义 EP029:把 List.Generate 落到工资条拆页与「固定行数切片筛选」。


继续学:打通数据全链路闭环

打通数据全链路闭环 · Excel BI 陪跑课

不只录播课,更是手把手带做实操。长按识别海报二维码咨询,或微信备注「Excel BI」。


© 版权声明
THE END
喜欢就支持一下吧
点赞1387 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容