如何在Power Query中动态将列内列表转换为行式数据
动态展开Power Query中等长列表列的解决方案
问题背景
给定如下Power Query代码,导入的JSON结构是外层数组仅包含一个对象,对象的每个属性值都是等长列表:
let Source = Json.Document("[{""var"" : [""var1"", ""var2""], ""visitor"" : [""vis1"", ""vis2""]}]"), myTab = Table.FromRecords(Source) in myTab
执行后生成的表格里,每列元素都是列表,而非单个值。需要实现完全动态的转换:不用指定列名或列数,自动把所有列的列表展开,最终效果和导入如下结构的JSON完全一致:
let Source = Json.Document("[{""var"" : ""var1"", ""visitor"" : ""vis1""},{""var"" : ""var2"", ""visitor"" : ""vis2""}]"), myTab = Table.FromRecords(Source) in myTab
实际处理的JSON文件结构固定(外层数组单对象,所有属性都是等长列表),但列名、列数、列表元素数量会动态变化,且所有列的列表长度始终保持一致。
解决方案代码
直接用以下代码即可实现动态转换,无需针对不同JSON修改核心逻辑:
let Source = Json.Document("[{""var"" : [""var1"", ""var2""], ""visitor"" : [""vis1"", ""vis2""]}]"), // 取出外层数组里的唯一对象 SingleRecord = Source{0}, // 动态获取所有字段名 FieldNames = Record.FieldNames(SingleRecord), // 取第一列列表的长度作为基准(所有列表长度一致) ListLength = List.Count(Record.Field(SingleRecord, FieldNames{0})), // 生成要遍历的索引列表 IndexList = {0..ListLength-1}, // 按索引遍历,把每一列对应位置的元素拼成新记录 TransformedRecords = List.Transform(IndexList, (idx) => Record.FromList( List.Transform(FieldNames, (field) => Record.Field(SingleRecord, field){idx}), FieldNames ) ), // 将记录列表转成最终表格 FinalTable = Table.FromRecords(TransformedRecords) in FinalTable
关键步骤说明
- 提取单条记录:
Source{0}直接取出外层数组里的唯一对象,不用关心数组长度(按需求结构固定为1)。 - 动态获取列名:
Record.FieldNames自动抓取所有属性名,完全不用硬编码列名,适配任意列数。 - 确定遍历范围:以第一列的列表长度为基准生成索引,因为所有列表长度一致,无需额外校验。
- 生成新记录列表:对每个索引,从所有列的列表中取出对应位置的元素,用
Record.FromList拼成和原属性名匹配的新记录。 - 转成表格:最后把新的记录列表转换成标准表格,得到目标效果。
扩展测试
针对多列多元素场景,只需要替换Source里的JSON字符串即可直接运行,核心逻辑完全不用改:
let Source = Json.Document("[{""var"" : [""var1"", ""var2"", ""var3"", ""var4""], ""visitor"" : [""vis1"", ""vis2"", ""vis3"", ""vis4""], ""customer"" : [""cus1"", ""cus2"", ""cus3"", ""cus4""]}]"), SingleRecord = Source{0}, FieldNames = Record.FieldNames(SingleRecord), ListLength = List.Count(Record.Field(SingleRecord, FieldNames{0})), IndexList = {0..ListLength-1}, TransformedRecords = List.Transform(IndexList, (idx) => Record.FromList( List.Transform(FieldNames, (field) => Record.Field(SingleRecord, field){idx}), FieldNames ) ), FinalTable = Table.FromRecords(TransformedRecords) in FinalTable
内容的提问来源于stack exchange,提问作者shZZ
相关产品推荐
相关产品推荐

