List.TransformMany

官方说明:

返回一个列表,其元素是从输入列表投影而来。将collectionTransform函数应用到每个元素,且调用resultTransform函数来构造结果列表。collectionSelector具有签名(x as Any) => ... 其中x是列表中的元素。 resultTransform投影结果的形状并具有签名(x as Any, y as Any) => ... 其中x是列表中的元素,y是通过将collectionTransform应用到该元素获得的元素。
List.TransformMany( list as list, collectionTransform as function, resultTransform as function) as list

解读:

今天要讲的这个函数,个人认为在所有M函数里难度排前10位,所以前方高能,请系好安全带。
看名字就知道,它是List.Transform的升级版,所以要理解这个函数请先弄明白它儿子。
说它较难,一方面是这个函数确实存在一定的理解难度,另一方面是因为上面那个官方说明根本不说人话,又没有案例,要是没人给讲一下的话自己看根本看不懂。下面来说说我对这个函数的理解:
List.TransformMany(参照列表,对列表元素进行的转换运算,转换前后数据的选择或自定义输出)

来看一个最简单的例子:= List.TransformMany({1..9},each {2},(x,y)=>x*y)

分两步计算,首先第二参数针对第一参数每个参考数给于一个转换赋值2,然后每个参考数字与对应的转换后的赋值数字进行乘积组合运算,并输出每一个乘积结果组合为一个list作为最终结果。

再来看一个:学校高中有三个年级,每个年级5个班,要列出所有的班级。

= List.TransformMany({"高一","高二","高三"},(a)=> {"1".."5"},(x,y)=>x&y&"班")
这次第二参数换个写法,用(a)=>而没用each,实际上都是同一个意思。看最后一个参数(x,y)=>,x表示第一参数里的list,y表示第二参数里的list,双数组循环遍历,形成笛卡尔积。先取x中第一个元素"高一",来遍历y里的每一个元素,得到"高一1班","高一2班","高一3班"等等,完了再取x中第二个元素,再次循环,所以最后的结果如图所示。

既然有each那就少不了_,当我们的第二参数写为each {_}或(x)=>{x},此时我们可以理解为没有将参考对象进行转换,但具体如何输出还得看第三参数。比如:
= List.TransformMany({1..9},each {_},(x,y)=>x+y)虽然第二参数未做转换,但是第三参数将前后重新组合一下输出在相应位置,输出的结果和第一张图一样。

再比如:= List.TransformMany({1..9},each {_,_},(x,y)=>x)
从这个例子要看到它这个函数装载数据的特性,他直接将每个参考对象进行了扩展重复一次,并且是直接展开非list压缩状态。

我们会反问,这里输出的是x即参考对象,为啥不是输出{1..9},这就是他另一个很重要的一个特性:
虽然看似最后输出的是参考对象,但是当第二参数对参考对象结构进行扩充变形时,输出的结果会以扩充变形的结构存在。
比如x中有m个元素,y中有n个元素,那么最终的结果就有m*n个元素。
你可以试一下= List.TransformMany({1..9},each {1,2},(x,y)=>x),第二参数不影响值的输出但影响了数据的结构。
以后你想重复拉伸数据直接把第二参数改成 each {1..你要重复的次数} 就可以了。

但你用这个:= List.TransformMany({1..9},each {1,2},(x,y)=>y)马上就能看到变化了,值以转换后的对象为准。
两个输出对象都是以第二参数扩充变形后的数据结构为基础,只是输出的值对象不同。

如果把第二参数改一下写成= List.TransformMany({1..9},each {_,null},(x,y)=>y),就会在每个元素下插入空格。

那么我们可以用这个思路解决一个很常见的实际问题————工资表转工资条:

= Table.FromRows(List.TransformMany(Table.ToRows(源),each {Table.ColumnNames(源),_},(x,y)=>y))

再来一个对每行进行多重聚合统计的形式:= List.TransformMany({{1..9},{2..10}},each {List.Sum,List.Average},(x,y)=>y(x))
先取x中第1个元素也就是{1..9},遍历y中每一个元素,经过求和和平均分别得到45和5,再取第2个元素遍历,得到54和6,所以结果为{45,5,54,6}。

还有个经典例子,九九乘法表:

= Table.FromColumns(List.TransformMany({1..9},each {{1..9}},(x,y)=>List.Transform(y,each if _>x then null else Text.Format("#{0}*#{1}=#{2}",{_,x,_*x}))))

还有一种递进式构造序列形式= List.TransformMany({1..9},each {1.._},(x,y)=>y)

针对分组统一转换处理逻辑再合并的情况= List.TransformMany({{1..9},{2..10}},List.Skip,(x,y)=>y)

因为结果都为比较长的list,我就不一一截图了,一遍两遍看不懂很正常,麻烦大家一一敲出来细细体会。

10 Replies to “List.TransformMany”

  1. 前二天在群里看图片学习了一下,现在写成文章内容更丰盛了,先赞了再说,周末再认真学习

  2. = List.TransformMany({{1..9},{2..10}},List.Skip,(x,y)=>y)
    最后一个y实际上也是y(x)吧?
    但是为什么不能直接写(x,y)=>y(x)?

    1. 第一参数的list中共有两个元素,第一次取第一个元素{1..9}去遍历第二参数,但第二参数并不是一个固定的list,而是参照第一参数动态生成的,得到{2..9}。第二次取第二个元素{2..10},第二参数为{3..10}。
      可以看到第二参数写的虽然是List.Skip,应该是个function,但是在遍历的时候,实际类型是list。
      y表示第二参数中list的每一个元素,也就是3,4,5这些,类型是number,y(x)肯定就不对了。

    2. = List.TransformMany({{1..9},{2..10}},each {List.Skip},(x,y)=>y(x)) 这么写可以生成俩个List,再展开就是上述的结果

  3. 工资表转工资条,用如下方法可增加空行隔开
    = Table.FromRows(List.TransformMany(Table.ToRows(源),each {Table.ColumnNames(源),_,List.Transform({1..Table.ColumnCount(源)},each "")},(x,y)=>y))

  4. 工资表转工资条:
    = Table.FromRows(List.TransformMany(Table.ToRows(源),each {Table.ColumnNames(源),_,List.Transform({1..Table.ColumnCount(源)},each "")},(x,y)=>y))
    用如上代码可以增加分隔栏。

  5. Table.FromColumns(List.Split(List.TransformMany({1..9},(y)=> {1..9},(x,y)=>if y<x then null else Text.From(x)&"x"&Text.From(y)&"="&Text.From( x*y)),9)) 九九乘法表,跟着大神学

  6. 我也上个有空行的工资条
    let
    源 = Excel.CurrentWorkbook(){[Name="表2"]}[Content],
    自定义1 = Table.FromRows(List.TransformMany(Table.ToRows(源), each{Table.ColumnNames(源),_,List.Repeat({null},Table.ColumnCount(源))},(x,y)=>y))
    in
    自定义1

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注