【问题标题】:Referencing a column name from an indexed list从索引列表中引用列名
【发布时间】:2021-07-17 17:16:06
【问题描述】:

背景: 我正在尝试创建包含动态列名的查询,这样如果用户更改表中列的名称,任何转换仍然遵循逻辑。

这个问题很简单,但很难用几句话来解释。我试图分解我所做的一切以轻松显示我遇到的问题。如果很明显,请直接跳到最后的问题。

动态列演示: 要开始使列名称动态化,我使用:

DynamicNameHeader = Table.ColumnNames(Source)

这会从原始表中创建一个列名列表。如果电子表格中的表名称发生更改,则此列表将动态更新。此列表可用于间接引用 M 代码中的列。

作为一个例子来说明这一点,在这里我将表 1 中的Column1 更改为Hello,并且在刷新数据后,输出的表会更新列以相应地读取Hello。

这可以通过引用DynamicNameHeader 列表并为所需列建立索引来实现。该代码还演示了简单的转换,可以通过将文本更改为大写并重新排序列来实现。

M 代码:

let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
   
DynamicHeaderNames =Table.ColumnNames(Source),

    #"Uppercased Text" = Table.TransformColumns(#"Source",{{DynamicHeaderNames{0}, Text.Upper, type text}, {DynamicHeaderNames{1}, Text.Upper, type text}}),
    #"Reordered Columns" = Table.ReorderColumns(#"Uppercased Text",{DynamicHeaderNames{1}, DynamicHeaderNames{0}})
in
    #"Reordered Columns"

这只是一个简单的示例,展示了我如何尝试以这种方式集成动态列。

问题 这是已在输出表中排序的实际数据的简单版本,因此第 1 列中的数据优先于第 2 列中的值。第 3 列中的数字只对应第 2 列中的值。

实现此输出的工作 M 代码:

let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],

    //combine the columns
    #"Added Custom" = Table.AddColumn(Source, "Custom", each
        let 
            L1 = if [Column2] = "-" then {[Column1]}
                    else List.Combine({{[Column1]},Text.Split([Column2],"#(lf)")}),
            L2 = if [Column2] = "-" then {[Column3]}
                    else List.Combine({{"-"},Text.Split([Column3],"#(lf)")})
        in 
            List.Zip({L1,L2})),
    #"Removed Columns1" = Table.RemoveColumns(#"Added Custom",{"Column1", "Column2", "Column3"}),

    //split the combined columns
    #"Expanded Custom" = Table.ExpandListColumn(#"Removed Columns1", "Custom"),
    #"Extracted Values" = Table.TransformColumns(#"Expanded Custom", 
        {"Custom", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
    #"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values", 
        "Custom", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {"Column1", "Column3"})
in
    #"Split Column by Delimiter"

我希望在简化示例中使此代码成为动态代码,但是在添加自定义列步骤时遇到了很多语法问题。

本质上,我希望通过像以前一样索引 DynamicNameHeader 列表来间接引用第 1、2 和 3 列。这可能吗?重要的是,这不仅允许用户更改列名,而且还允许对数据的转换也可以动态地引用相关列。这个自定义列转换几乎是唯一被证明很困难的步骤,因为它使用了似乎与 DynamicNameHeader{x} 不兼容的 []。

我希望这个解释足够清楚,可以理解我想要实现的目标,如果有人对此问题有任何解决方案,我将不胜感激。

【问题讨论】:

  • 如果用户更改表中列的名称?!
  • 嗨,是的,我认为这是有道理的?因此,例如,当我从空单元格创建一个新查询时,将使用 Column1、Column2 等创建一个表。通常,如果要将 Column1 更改为不同的名称,则查询会中断。目前,如果源表有,我让输出表自动更改列名。我只是希望在同一步骤中启用此转换。
  • 我建议您编辑您的问题,通过发布您希望处理的数据示例(最好是文本而不是屏幕截图),并说明您如何处理想要它排序,以及你想要的结果的一个例子。
  • @RonRosenfeld 您好,我已经更新了这个问题。它很长,因为我觉得我解释的问题不够清楚。也因为我不确定如何发布数据而不会使它像文本一样变得非常混乱。相反,我使示例变得非常简单。想要的结果已经达到了,我就是想看看能不能动态的完成。
  • 编辑了我的答案以显示适用于您的数据的代码。

标签: excel dynamic excel-formula conditional-statements powerquery


【解决方案1】:

您发布的 M 代码 并没有真正返回您显示的结果,但这里有一个示例,说明如何在您的自定义列中使用未定义的列标题。

这涉及添加一个Index 列来整理。

请注意,我还替换了示例数据中的空值,因为 Text.Split 函数将失败并返回空值

    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],

allCols=Table.ColumnNames(Source),
    col1 = allCols{0},
    col2 = allCols{1},
    col3 = allCols{2},

IDX = Table.AddIndexColumn(Source,"IDX",0,1),
    #"Replaced Value" = Table.ReplaceValue(IDX,null,"",Replacer.ReplaceValue,allCols),
    #"Added Custom" = Table.AddColumn(#"Replaced Value", "lists", each let 
            col2List = Text.Split(
                Table.Column(#"Replaced Value",col2){[IDX]},
                 "#(lf)"),
            col3List = Text.Split(
                Text.From(Table.Column(#"Replaced Value",col3){[IDX]}),
                "#(lf)")
        in 
            List.Zip({col2List,col3List}))

编辑

这是一个对实际列名不敏感的 M-Code 示例,它将根据您显示的输入生成您显示的输出:

let
    Source = Excel.CurrentWorkbook(){[Name="Table10"]}[Content],

allCols=Table.ColumnNames(Source),
    col1 = allCols{0},
    col2 = allCols{1},
    col3 = allCols{2},

IDX = Table.AddIndexColumn(Source,"IDX",0,1),
#"Added Custom" = Table.AddColumn(IDX, "Custom", each if Table.Column(IDX,col2){[IDX]} = "-" 
    then 
       List.Zip({{Table.Column(IDX,col1){[IDX]}},{Text.From(Table.Column(IDX,col3){[IDX]})}}) 
    else 
      List.InsertRange(
        List.Zip({
        Text.Split(Table.Column(IDX,col2){[IDX]},"#(lf)"),
        Text.Split(Table.Column(IDX,col3){[IDX]},"#(lf)")}),
            0,{{Table.Column(IDX,col1){[IDX]},"-"}})),

    #"Removed Columns" = Table.RemoveColumns(#"Added Custom",List.Combine({{"IDX"}, allCols})),
    #"Expanded Custom" = Table.ExpandListColumn(#"Removed Columns", "Custom"),
    #"Extracted Values" = Table.TransformColumns(#"Expanded Custom", 
        {"Custom", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
    #"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values", "Custom", 
        Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {col1, col3}),
    #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{col1, type text}, {col3, type text}})
in
    #"Changed Type"

【讨论】:

  • 您好,您确定我发布的代码不适合您吗?我刚刚从这里复制了它,它已经生成了完全相同的绿色表。也感谢您的回答,但我不确定它是否产生相同的输出?
  • 啊,我刚刚看到你的编辑来了,会看看。
  • @Nick 我认为原因是在我第一次使用您发布的代码时,有连字符而不是空单元格。反正我最后贴的代码应该可以用了。
  • 似乎有效!再次感谢:) 我需要研究它才能正确理解发生了什么。出于兴趣,您介意解释我做错了什么吗? IE。为什么我不能只将 DynamicHeaderName{x} 插入 [] 另外,索引列在这里是如何工作的?
  • @Nick 在添加自定义列时,索引列确保您只查看同一行中的单元格,以便拆分然后合并。至于输入[],我不太确定。但是col1 是一个字符串。所以也许程序将其视为["col1"] 而不是,因为我们更喜欢[col1]。但是Table.Column(tbl,col1){[Index]} 将返回该特定行中列的内容,这通常是我们在添加自定义列时想要的。也许知识渊博的人会有更好的解决方案或解释。
猜你喜欢
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 2020-07-27
  • 2013-03-30
  • 1970-01-01
  • 2012-03-27
  • 2016-05-21
  • 1970-01-01
相关资源
最近更新 更多