DAX's PATH function equivalent Custom Column in Power Query
I wrote one that could do ~100,000 rows in ~20 seconds. Appends a column "PATH" to a table. Inspired by this BIAccountant post: https://www.thebiccountant.com/2021/02/10/guest-post-using-list-accumulate-for-input-output-genealogy/, but modified to be recursive.
A record at the top of the hierarchy should have a null in the Parent field. There is no loop protection, so you don't want any records to be their own parents, their own grandparents, etc.
Note that you only invoke the function once on the table as whole, not row-by-row. Add a step along the lines of:
= fnAddPath(#"Previous Step", "ParentID Column Name", "SelfID Column Name")
(Self_Referential_Table as table, Parent_Column_Name as text, Self_Column_Name as text) =>
let
#"Renamed Columns" = Table.RenameColumns(Self_Referential_Table, {{Self_Column_Name, "0"}, {Parent_Column_Name, "1"}}),
Buffered = Table.Buffer(Table.SelectColumns(
Table.RenameColumns(Self_Referential_Table,{{Self_Column_Name, "Child"},{Parent_Column_Name, "Parent"}}),{"Child", "Parent"}))
in
let
GetParents = (state as table, currentLevelFromLeaf as number) =>
let
NextParents = Table.ExpandTableColumn(
Table.NestedJoin(state, {Text.From(currentLevelFromLeaf)},
Buffered, {"Child"}, "NextLevel", JoinKind.LeftOuter),
"NextLevel", {"Parent"}, {Text.From(currentLevelFromLeaf + 1)}),
result = if List.NonNullCount(Table.Column(NextParents,Text.From(currentLevelFromLeaf + 1))) = 0 then
[resultTable = NextParents, maxHeight = currentLevelFromLeaf]
else
@GetParents(NextParents, currentLevelFromLeaf + 1)
in
result,
//Reformat the Output
ResultOutput = GetParents(#"Renamed Columns", 1),
ListOfLevels = List.Transform(List.Numbers(Record.Field(ResultOutput, "maxHeight") + 1,Record.Field(ResultOutput, "maxHeight") + 2, -1), each Number.ToText(_)),
OutputTable = Table.AddColumn(Record.Field(ResultOutput, "resultTable"), "PATH",
each Text.Combine(List.Transform(ListOfLevels, (x) => Record.Field(_, x)), "|")),
OutputTable2 = Table.RemoveColumns(Table.RenameColumns(
OutputTable, {{"0", Self_Column_Name}, {"1", Parent_Column_Name}}),
List.RemoveMatchingItems(ListOfLevels,{"0","1"}))
in
OutputTable2
Probably the final Reformatting could be done more cleverly and in fewer lines of code.
- mloyalka19963 years agoFrequent Visitor
Hi Scott, Thank you for the code. I have a couple of question regarding the funvtion you have written. A small question what does it mean when u say "A topmost record should have a null in the Parent field". do i need to add a record ?
- Scott_Parker3 years agoFrequent Visitor
I rephrased it. I meant a record at the top of the hierarchy, meaning it has no parents, should have Parent = null, rather than Parent = Self because it would result in infinite recursion.
- mloyalka19963 years agoFrequent Visitor
thanks for that Scott. I created a coloumn called task_final. so whenevr Parent=self i replaced the value of child to null or kept the child value. Now when i am running the function with the following parameters. I still get an error. Any suggestion?