I received a question in the Power BI Community today. The requirement is to automatically sort the columns according to different values of the product column (illustration is provided below). I quickly thought that it can be solved by just defining a PowerQuery function, and here I will put this small Share your skills with everyone.
need
As shown in the figure below, the NUMBER column is divided according to the ID column (product number). As can be seen from the figure, this is not a perspective, and it requires that whenever a new product number is added to the ID column, a new column can be automatically added, and so on.
data
Open the PowerQuery editor and simulate a sample data:
let
DATA =
Table.FromRecords(
{
[ID = "A", Number = 12],
[ID = "A", Number = 5],
[ID = "A", Number = 6],
[ID = "A", Number = 8],
[ID = "B", Number = 14],
[ID = "B", Number = 9],
[ID = "B", Number = 7],
[ID = "B", Number = 7],
[ID = "C", Number = 5],
[ID = "C", Number = 16],
[ID = "C", Number = 18]
},
type table [ID = nullable text, Number = nullable number]
)
in
DATA
As shown in the picture:
plan
The overall idea is to use the M statement to define a function, with ID as the only parameter of the function. Whenever a parameter is passed, for example, when the ID is A, the original data can be filtered by this parameter into a data table with only A products, and then just rename the NUMBER column to a parameter. When the parameters are B, C, D, E..., and so on, so the new NUMBER column name will also be B, C, D, E..., PowerQuery recognizes that these are different columns, and will automatically create new columns when merging data, so It solved the problem perfectly.
step
define function
Here you only need to define one parameter and complete two steps. Filter and rename as mentioned above:
(ID_Code as text) =>
let
DATA =
DATA,
DATA_Filtered = Table.SelectRows(DATA, each ([ID] = ID_Code)),
DATA_Renamed = Table.RenameColumns(DATA_Filtered,{
{"Number", ID_Code}})
in
DATA_Renamed
Substitute any parameters and test without error:
Create a new parameter table
We need to continuously pass parameters to the function in order to get a complete table, so we need to take the ID column and remove duplicates to generate a parameter table:
let
Source = DATA,
#"Removed Columns" = Table.RemoveColumns(Source,{"Number"}),
#"Removed Duplicates" = Table.Distinct(#"Removed Columns"),
#"Invoked Custom Function" = Table.AddColumn(#"Removed Duplicates", "demo", each demo([ID])),
#"Removed Columns1" = Table.RemoveColumns(#"Invoked Custom Function",{"ID"}),
#"Expanded demo" = Table.ExpandTableColumn(#"Removed Columns1", "demo", {"ID", "A", "B", "C"}, {"ID", "A", "B", "C"})
in
#"Expanded demo"
as follows:
Call function to complete column sorting
Select the parameter table, then operate as shown below to call the function we created previously and expand the resulting columns:
You can also run the following code directly:
let
Source = DATA,
#"Removed Columns" = Table.RemoveColumns(Source,{"Number"}),
#"Removed Duplicates" = Table.Distinct(#"Removed Columns"),
#"Invoked Custom Function" = Table.AddColumn(#"Removed Duplicates", "demo", each demo([ID])),
#"Removed Columns1" = Table.RemoveColumns(#"Invoked Custom Function",{"ID"}),
#"Expanded demo" = Table.ExpandTableColumn(#"Removed Columns1", "demo", {"ID", "A", "B", "C"})
in
#"Expanded demo"
The results are as follows, in line with expectations:
test
Now we append new data [D] to the data source:
Running the query, we find that the latest column [column D] has not been created. This is because when expanding the NUMBER column, its parameters are fixed. We need to let the Table.ExpandTableColumn function dynamically obtain the complete ID list. This step is not difficult. We only need to convert the parameter table into a list and assign it to the new variable [ID]:
ID = #"Removed Duplicates"[ID]
Then replace the following part of the original code:
= Table.ExpandTableColumn(#"Removed Columns1", "demo", {"ID", "A", "B", "C"})
Just change it to the following statement.
= Table.ExpandTableColumn(#"Removed Columns1", "demo", ID)
Running the query again, all columns are generated dynamically as expected and the problem is perfectly resolved.