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

A new Data Days event is coming soon! This time we’re going bigger than ever. Fabric, Power BI, SQL, AI and more. Don't miss out.

Reply
isg
Frequent Visitor

Help with Tree Traversal in Power Query

Hi everyone,

 

First of all, full disclosure: I am not at all a Power Query expert. I had a great success getting a solution by posting in this forum last time so I'm posting again in hopes that you all Power Query experts would be able to help me with another problem.

 

I essentially have a data set that is supposed to look like a tree. Below is an example:

 

Tree Problem.png

The corresponding data to the above chart is as below (sorry, formatting is difficult in this forum).

 

PrimaryKeyData1Data2Data3 ForeignKeyFK Data1FK Data2FK Data3
0001Data1_1Data2_1Data3_1     
0002Data1_2Data2_2Data3_2     
0003Data1_3Data2_3Data3_3     
0004Data1_4Data2_4Data3_4 0001Data1_1Data2_1Data3_1
0005Data1_5Data2_5Data3_5 0001Data1_1Data2_1Data3_1
0006Data1_6Data2_6Data3_6 0004Data1_4Data2_4Data3_4
0007Data1_7Data2_7Data3_7 0005Data1_5Data2_5Data3_5
0008Data1_8Data2_8Data3_8 0002Data1_2Data2_2Data3_2
0009Data1_9Data2_9Data3_9 0002Data1_2Data2_2Data3_2
0010Data1_10Data2_10Data3_10 0002Data1_2Data2_2Data3_2
0011Data1_11Data2_11Data3_11 0002Data1_2Data2_2Data3_2
0012Data1_12Data2_12Data3_12 0009Data1_9Data2_9Data3_9
0013Data1_13Data2_13Data3_13 0009Data1_9Data2_9Data3_9
0014Data1_14Data2_14Data3_14 0011Data1_11Data2_11Data3_11
0015Data1_15Data2_15Data3_15 0003Data1_3Data2_3Data3_3
0016Data1_16Data2_16Data3_16 0003Data1_3Data2_3Data3_3
0017Data1_17Data2_17Data3_17 0003Data1_3Data2_3Data3_3
0018Data1_18Data2_18Data3_18 0003Data1_3Data2_3Data3_3
0019Data1_19Data2_19Data3_19 0003Data1_3Data2_3Data3_3

 

I want to be able to input the PrimaryKey value, and get all the data "under" the value inputted based on the tree chart. Below is an example input and expected output.

 

Data Input:

Search PrimaryKey
0001
0009
0015

 

Data Output:Tree Problem_Highlighted.png

 

PrimaryKeyData1Data2Data3 ForeignKeyFK Data1FK Data2FK Data3
0001Data1_1Data2_1Data3_1     
0004Data1_4Data2_4Data3_4 0001Data1_1Data2_1Data3_1
0005Data1_5Data2_5Data3_5 0001Data1_1Data2_1Data3_1
0006Data1_6Data2_6Data3_6 0004Data1_4Data2_4Data3_4
0007Data1_7Data2_7Data3_7 0005Data1_5Data2_5Data3_5
0009Data1_9Data2_9Data3_9 0002Data1_2Data2_2Data3_2
0012Data1_12Data2_12Data3_12 0009Data1_9Data2_9Data3_9
0013Data1_13Data2_13Data3_13 0009Data1_9Data2_9Data3_9
0015Data1_15Data2_15Data3_15 0003Data1_3Data2_3Data3_3

 

So, if the input is 0001, 0002, 0003, it would output everything.

 

Currently, I am doing a manual iteration where:

  1. I merge input with the PrimaryKey
  2. Merge input with the ForeignKey then append to #1
  3. Then I merge the appended table to the ForeignKey again and append that to #2.
  4. Remove duplicates.
  5. Then I merge input again, etc..

This is not an ideal algorithm as I would not have to manually stop the merging + append + deduplication. I want to the script to basically do that automatically for me when it sees that there are no other "nodes" to reiterate through.

 

My idea is to have a reiteration with a counter, so when the count of rows in the output table = the number of rows after deduplication, it will stop the reiteration. However, I have no idea how to set that "while" recursion in M. Is this something that would be possible in Power Query? Or should I view the data / procedure in a different perspective?

 

Let me know if I am not making any sense with my questions and I can edit the post to clarify!

3 REPLIES 3
ThxAlot
Super User
Super User

A side question, are you engaged in data analysis related job?

 

Your question is of typical graph traversal algorithm type. I myself once spent a whole weekend to implement the most classic "depth first search" algorithm in PQ (although PQ isn't an ideal tool to handle recursion)

let
    DFSfx = (_nodes as list) =>
    List.TransformMany(
        _nodes,
        each let neighbors = Record.FieldOrDefault(graph, _, null) in if neighbors is null then {{}} else @DFSfx(neighbors),
        (x,y) => {x} & y
    ),

    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjAwMFTSUVKK1QGzTYBssBCUb4rGN4PwTWB8cwjfFMa3hPCNoHxDIwjfEsY3RuNDzTdWio0FAA==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [PrimaryKey = _t, ForeignKey = _t]),
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"PrimaryKey", type text}, {"ForeignKey", type text}}),
    #"Grouped by ForeignKey" = Table.Group(#"Changed Type", "ForeignKey", {"Value", each Table.Column(_, "PrimaryKey")}),
    graph = Record.FromTable(Table.RenameColumns(#"Grouped by ForeignKey", {{"ForeignKey","Name"}})),
    Traversal = DFSfx({"0001","0009","0015"})
in
    Traversal

ThxAlot_0-1780605300220.png

ThxAlot_1-1780605378141.png

ThxAlot_2-1780605420648.png



Expertise = List.Accumulate(


        {Days as from Today},


        {Skills and Knowledge},


        (Current, Everyday) => Current & Day.LearnAndPractise(Everyday)


)



slorin
Super User
Super User

Hi

Another solution

let
Source = YourSource,
Keys = Record.FromList(Source[ForeignKey], Source[PrimaryKey]),
Search = {"0001","0009","0015"},
Test = (Actual) as logical=>
if Actual = null then false
else if List.Contains(Search, Actual) then true
else @Test(Record.FieldOrDefault(Keys, Actual, null)),
Result = Table.SelectRows(Source, each Test([PrimaryKey]))
in
Result

 

slorin_1-1780476865691.png

 

 

Stéphane

oussamahaimoud
Solution Sage
Solution Sage

Hi @isg,

Hope you're doing well!

 

I had to solve this problem before.I recommend you to Use List.Generate to iteratively expand the set of matched PKs by checking which rows have a ForeignKey that's already in your matched set. Stop when no new rows are added (i.e., the count stabilizes).

 

So, paste this as a new blank query (M):

 

let

    // Your source tables — adjust names as needed

    Source = YourDataTable,

    InputKeys = YourInputTable, // single column: [SearchPrimaryKey]

 

    // Convert input to a list

    SeedKeys = List.Transform(InputKeys[SearchPrimaryKey], Text.From),

 

    // Iteratively expand the key set

    ExpandedKeys = List.Last(

        List.Generate(

            () => SeedKeys,

            (current) => 

                List.Count(

                    List.Union({

                        current,

                        List.Transform(

                            Table.SelectRows(

                                Source, 

                                each List.Contains(current, Text.From([ForeignKey]))

                            )[PrimaryKey],

                            Text.From

                        )

                    })

                ) > List.Count(current),

            (current) =>

                List.Union({

                    current,

                    List.Transform(

                        Table.SelectRows(

                            Source, 

                            each List.Contains(current, Text.From([ForeignKey]))

                        )[PrimaryKey],

                        Text.From

                    )

                })

        )

    ),

 

    // Filter source to only matched rows

    Result = Table.SelectRows(Source, each List.Contains(ExpandedKeys, Text.From([PrimaryKey])))

 

in

    Result

 

For large datasets, this can be slow since each iteration re-scans the full table. If performance is a concern, converting the source to a list of records before the loop will help significantly.

 

Assisted by AI for clarity of wording.

 

Hope this helps! Don't forget to accept as solution and like it 👍 in order to keep helping others.

 

Best regards,

Oussama (Data Consultant - Expert Fabric & Power BI)


  Did my response help you? Clicking Kudos is a small gesture that goes a long way, it encourages contributors and helps the community thrive!


Did I answer your question? Please mark my post as a Solution, it helps others find the answer faster.


Senior Data & BI Consultant · Microsoft Fabric & Power BI Specialist


Connect with me on LinkedIn

Helpful resources

Announcements
May Power BI Update Carousel

Power BI Monthly Update - May 2026

Check out the May 2026 Power BI update to learn about new features.

Fabric SQL PBI Data Days

Data Days 2026 coming soon!

Sign up to receive a private message when registration opens and key events begin.

New to Fabric survey Carousel

New to Fabric Survey

If you have recently started exploring Fabric, we'd love to hear how it's going. Your feedback can help with product improvements.

Power BI DataViz World Championships carousel

Power BI DataViz World Championships - June 2026

A new Power BI DataViz World Championship is coming this June! Don't miss out on submitting your entry.