Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
niark
Frequent Visitor

Get hierarchical item position

Hello,

 

I have a complex problem (in my mind) to solve with hierachical data.

 

My data are like this :

ParentChildPosition
530
540
372441
399841
31581
15844672


The [Position] column has to be filled with the position (indexed to 0 or 1) of the [Parent] value. here you can see that [Parent] 5 is parent of [Child] 3 and [Child] 3 parent of multiple [Child].

In the example, [Parent] 5 has no parent so the value is 0 and so on...

 

I'm able to find what I want with Excel and manual process but I don't have yet the "PBI mind" to do it...

I'm using this formula with excel :

{=IF([@Position]="NotProcessed";COUNTIFS([Position6];TRUE;[Child];[@Parent]);FALSE)}

With the result, I create a new column ei:Position7

 

Can someone try to help me ?

 

1 ACCEPTED SOLUTION
MarcelBeug
Community Champion
Community Champion

[Counter] < 100 is to end after the 100th loop.

 

I tested with 10,000 records, which took about 100 seconds.

 

With additional Table.Buffers (inside List.Generate), runtime decreased to about 60 seconds:

 

let
    Source = Table.Buffer(Data),

    TablesPerPosition = List.Generate(
        () => [Counter = -1, MergedTable = #table(0,{}), NewPart = #table({"Parent"},{{0}}), Remaining = Source],
        each (not Table.IsEmpty([NewPart])) and [Counter] < 100,
        each [Counter     = [Counter] + 1,
              MergedTable = let
                                Merged = Table.NestedJoin([Remaining],{"Parent"},[Remaining],{"Child"},"Data",JoinKind.LeftOuter),
                                AddedPosition = Table.AddColumn(Merged, "Position", each if Table.IsEmpty([Data]) then Counter else null, Int64.Type),
                                RemovedColumn = Table.RemoveColumns(AddedPosition,{"Data"})
                            in
                                Table.Buffer(RemovedColumn),
              NewPart     = Table.Buffer(Table.SelectRows(MergedTable, each ([Position] <> null))),
              Remaining   = let
                                Filtered = Table.SelectRows(MergedTable, each ([Position] = null)),
                                RemovedColumn = Table.RemoveColumns(Filtered,{"Position"})
                            in 
                                Table.Buffer(RemovedColumn)],
        each [NewPart]),
    TablesCombined = Table.Combine(List.Skip(TablesPerPosition))
in
    TablesCombined
Specializing in Power Query Formula Language (M)

View solution in original post

4 REPLIES 4
MarcelBeug
Community Champion
Community Champion

A possible Power Query solution below.

 

List.Generate is used to loop over the remaining part of the table. In each iteration, the remaining part is merged with itself (with Parent and Child as keys) and the Position is added if the Merged table is empty (i.e. the Parent is not found as Child).

Table NewPart will be the records in which the Position is not null.

The new Remaining part are the records with Position null.

List.Generate returns all NewParts, including the first dummy NewPart table which is skipped when the tables are combined in the last step.

 

The loop continues while the remaining part still has records (and Counter < 100, just to prevent endless looping).

 

let
    Source = Table.Buffer(Data),

    TablesPerPosition = List.Generate(
        () => [Counter = -1, MergedTable = #table(0,{}), NewPart = #table({"Parent"},{{0}}), Remaining = Source],
        each (not Table.IsEmpty([NewPart])) and [Counter] < 100,
        each [Counter     = [Counter] + 1,
              MergedTable = let
                                Merged = Table.NestedJoin([Remaining],{"Parent"},[Remaining],{"Child"},"Data",JoinKind.LeftOuter),
                                AddedPosition = Table.AddColumn(Merged, "Position", each if Table.IsEmpty([Data]) then Counter else null, Int64.Type),
                                RemovedColumn = Table.RemoveColumns(AddedPosition,{"Data"})
                            in
                                RemovedColumn,
              NewPart     = Table.SelectRows(MergedTable, each ([Position] <> null)),
              Remaining   = let
                                Filtered = Table.SelectRows(MergedTable, each ([Position] = null)),
                                RemovedColumn = Table.RemoveColumns(Filtered,{"Position"})
                            in 
                                Table.Buffer(RemovedColumn)],
        each [NewPart]),
    TablesCombined = Table.Combine(List.Skip(TablesPerPosition))
in
    TablesCombined
Specializing in Power Query Formula Language (M)

Hi Marcel,

 

Many thanks for this clear explanation.

Regarding the criteria [counter]< 100, is it there to end the loop at the 100th loop ? or if a Position contains 100 items ?

 

I have another question regarding performances, I have more than 7K rows to process and 8 different positions and it take a long time to calculate. Do you have any tips to have it done quicker ?

 

This is definitely the good solution.

 

Thanks again.

MarcelBeug
Community Champion
Community Champion

[Counter] < 100 is to end after the 100th loop.

 

I tested with 10,000 records, which took about 100 seconds.

 

With additional Table.Buffers (inside List.Generate), runtime decreased to about 60 seconds:

 

let
    Source = Table.Buffer(Data),

    TablesPerPosition = List.Generate(
        () => [Counter = -1, MergedTable = #table(0,{}), NewPart = #table({"Parent"},{{0}}), Remaining = Source],
        each (not Table.IsEmpty([NewPart])) and [Counter] < 100,
        each [Counter     = [Counter] + 1,
              MergedTable = let
                                Merged = Table.NestedJoin([Remaining],{"Parent"},[Remaining],{"Child"},"Data",JoinKind.LeftOuter),
                                AddedPosition = Table.AddColumn(Merged, "Position", each if Table.IsEmpty([Data]) then Counter else null, Int64.Type),
                                RemovedColumn = Table.RemoveColumns(AddedPosition,{"Data"})
                            in
                                Table.Buffer(RemovedColumn),
              NewPart     = Table.Buffer(Table.SelectRows(MergedTable, each ([Position] <> null))),
              Remaining   = let
                                Filtered = Table.SelectRows(MergedTable, each ([Position] = null)),
                                RemovedColumn = Table.RemoveColumns(Filtered,{"Position"})
                            in 
                                Table.Buffer(RemovedColumn)],
        each [NewPart]),
    TablesCombined = Table.Combine(List.Skip(TablesPerPosition))
in
    TablesCombined
Specializing in Power Query Formula Language (M)

I'm more around 5 minutes than 100s. I think it comes from my machine.

 

thanks again for your help.

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.