先来个题外话:我个人感觉天朝无论是政府还是普通民众还是企业,规范意识都相当糟糕。
这个题外话和这篇帖子的主题有什么关系呢?那就是,明明有统一的数据模板,但就是不愿意提供给客户去填写;客户方呢,明明提供了模板给ta,ta就是喜欢把你的模板丢一边,发一些让你看了头大的数据过来。
我现在面临的一个场景是:我需要把客户提供过来的数据,整理成我们系统可识别的,然后导入系统。但是客户提供过来的数据呢,同一个字段可能叫“手机”,也可能叫“手机号码”,也可能叫“联系电话”,也可能叫“电话”,还可能叫联系方式“,搞不好还冒点英文叫“mobile”。客户提供的同样数据,简直达到了随心所欲从不守规矩的地步。这就导致我建立的Powerquery模板随时阵亡。
作为懒人,我当然不愿意每次手工去修改列名。所以,有三个办法供我选择:
一是用if来判断列名中是否包含特定字段
如果包含我需要的字段,那么执行代码块A;如果不包含我需要的字段,那么执行代码块B。比如下面这行代码:
=if List.ContainsAny(Table.ColumnNames(buffer),{"性别","sex","Sex","SEX"}) then Table.RenameColumns(设置email等,{List.First(List.Range(Table.ColumnNames(设置email等),List.PositionOfAny(Table.ColumnNames(设置email等),{"性别","sex","Sex","SEX"}),1)),"性别"}) else 设置email等
我用 List.ContainsAny()来检查列名中是否包含是否包含{"性别","sex","Sex","SEX"}中的任意一个,如果包含,那么我就将其重命名为“性别”。这样做的依据是虽然客户会对字段乱命名,但是每一个字段乱命名的范围是有限的,而且我假定了每个字段都是唯一的,不会有重名或一个字段被叫几个名字的情况存在。
二是在展开列时,用类似于Table.ColumnNames()的函数来获取列名
在处理过程中,经常会遇到展开列的情况。如果直接在UI点击展开列的按钮,那么生成的代码,列名是硬编码的,这时,如果客户提供的数据中有任何一个字段名字和模板中的名字不一样,就会导致模板出错。所以,需要把展开列的代码块中列名进行软编码。
这主要用的是获取列名或记录的字段名称的函数。如下面两段代码所示,分别展开了由记录构成的表格的列和由列构成的表格的列。
= Table.ExpandRecordColumn(删除的其他列, "自定义", Record.FieldNames(删除的其他列[自定义]{0}),Record.FieldNames(删除的其他列[自定义]{0}))
或者
= Table.ExpandTableColumn(删除的其他列1, "自定义", Table.ColumnNames(删除的其他列1[自定义]{0}),Table.ColumnNames(删除的其他列1[自定义]{0}))
三是结合Table.SelectColumns()和,Table.ColumnNames()选择我需要的列
客户提供的数据字段中有很多是我们不需要的,我只想保留我需要的字段。比如下面这个例子,对客户数据处理后的表格中,包含系统模板所需的字段,还包括多余的字段,我从其他地方获取到模板的列名,然后只选择表格中包含这些列名的列,就排除了那些我不需要的列,无论这些列的名字是什么。
= Table.SelectColumns(设置根节点编码,Table.ColumnNames(Table.PromoteHeaders(源之系统导入模板{[Item="用户导入模板",Kind="Sheet"]}[Data])))
反思:如果只处理一次,客户数据文档只有一个,还不如手动重命名原始文件来得快和方便。当客户数据有无数个,或者需要无数次处理的时候,这种动态化将减少一些重复劳动。