Unpivoting multiple alternate rows

HI All, I have a tricky problem and have tried multiple ways, but couldn’t get around it.
I need to unpivot the store names into one column and in the adjacent column I need the value from the T&F column either Yes or No. All the T&F, T&F1, T&F2 can be combined into just T&F, but the value is important alongside the store name. Please see image below:
I have also attached the file.


Matrix.xlsx (3.7 MB)

Thanks,
Mustafa

*** READ FIRST ***

Before you send your question, make sure that all details relevant to your question is complete:

  • Your current work-in-progress PBIX file - VERY IMPORTANT
  • A clear explanation of the problem you are experiencing
  • A mockup of the results you want to achieve
  • Your underlying data file

Check out this thread on Tools and Techniques for Providing PBIX Files with Your Forum Questions

Also make sure that your data file contains no confidential information. If it does, click the link above.

*** DELETE THIS MESSAGE IF YOU ARE SURE ALL YOUR DETAILS ARE COMPLETE OR IF THE ABOVE INFORMATION IS NOT APPLICABLE TO YOUR QUESTION.***

Hi @chris786 - Can you please check the attached file and see if the Solution provided helps you.

Store Solution.pbix (89.0 KB)

Thanks
Ankit J

Please find attached the SQL and PowerQuery function for your reference. Also, I have applied the Power Query function to your sample Excel file too.
Here’s a breakdown of how the UnpivotStores Power Query function works, step by step:


:wrench: Function Purpose

The function takes a wide-format table (with many store columns and their corresponding T&F columns) and transforms it into a long-format table with three dynamic columns:

  • Store Name
  • Assortment (value from the store column)
  • TF (value from the corresponding T&F column)

It also retains the first 12 static columns and filters out rows where both Assortment and TF are '0', 'NO', or null.


:brain: Step-by-Step Explanation

1. Function Definition

UnpivotStores = (rawTable as table) =>

Defines a function named UnpivotStores that takes a table (rawTable) as input.


2. Static Columns

staticCols = {
    "CAT", "Ctegory", "Group Name", "Sub-Family", "Brand-Name", "Barcode", "Article", "Model No",
    "POWER SKU", "MSL QTY", "Reg-SP", "SP+VAT"
}

These are the columns that remain unchanged in the output.


3. Store–T&F Pairs

storePairs = {
    {"Dubai Mall-DM1- 1404", "T&F"},
    {"DCC-D01-1402", "T&F2"},
    ...
}

Each pair represents a store column and its corresponding T&F column.


4. Transform Each Pair

transformedTables = List.Transform(storePairs, each
    let
        storeCol = _{0},
        tfCol = _{1},
        selectedCols = Table.SelectColumns(rawTable, staticCols & {storeCol, tfCol}),
        renamed = Table.RenameColumns(selectedCols, {{storeCol, "Assortment"}, {tfCol, "TF"}}),
        addedStore = Table.AddColumn(renamed, "Store Name", each storeCol),
        filtered = Table.SelectRows(addedStore, each not (
            List.Contains({"0", "NO", null}, [Assortment]) and
            List.Contains({"0", "NO", null}, [TF])
        ))
    in
        filtered
)

This block does the heavy lifting:

  • Selects the static columns + current store and T&F columns.
  • Renames the store column to Assortment and T&F column to TF.
  • Adds a new column Store Name with the store name as a string.
  • Filters out rows where both Assortment and TF are '0', 'NO', or null.

5. Combine All Transformed Tables

combined = Table.Combine(transformedTables)

Merges all the filtered tables into one long-format table.


6. Return the Result

in combined

Returns the final unpivoted and filtered table.


:white_check_mark: Benefits of This Approach

  • Reusable: You can apply it to any similar dataset by calling UnpivotStores(raw_data).
  • Clean: Keeps logic modular and readable.
  • Flexible: Easy to update store–T&F pairs or static columns.

Hope this helps
unpivot_sql_query.sql (8.5 KB)
Unpivot_pq_query.pq (2.0 KB)
Matrix_test.xlsx (261.6 KB)

I have created an updated function to allow for dynamically varied number of static columns as well as avoid hard coding the column headers and thereby making them flexible and reusable in other instances too.


:brain: How the Updated Function Works — Step-by-Step

1. Function Signature

UnpivotStoresDynamic = (rawTable as table, staticColumnCount as number) =>

This defines a reusable function that takes:

  • rawTable: your original wide-format table.
  • staticColumnCount: the number of columns at the beginning that should remain unchanged.

2. Extract Column Names

allColumns = Table.ColumnNames(rawTable)

Gets a list of all column names in the table.


3. Split into Static and Dynamic Columns

staticCols = List.FirstN(allColumns, staticColumnCount)
dynamicCols = List.Skip(allColumns, staticColumnCount)
  • staticCols: the first staticColumnCount columns.
  • dynamicCols: the remaining columns, assumed to alternate between store and T&F.

4. Pair Store and T&F Columns

dynamicPositions = List.Positions(dynamicCols)
storePairs = List.Transform(dynamicPositions, (i) =>
    if Number.Mod(i, 2) = 0 and i + 1 < List.Count(dynamicCols) then
        {dynamicCols{i}, dynamicCols{i + 1}}
    else
        null
)
validPairs = List.RemoveNulls(storePairs)
  • Iterates through positions in dynamicCols.
  • For every even index i, pairs dynamicCols{i} (store) with dynamicCols{i+1} (T&F).
  • Filters out any invalid or incomplete pairs.

5. Transform Each Store–T&F Pair

transformedTables = List.Transform(validPairs, each
    let
        storeCol = _{0},
        tfCol = _{1},
        selectedCols = Table.SelectColumns(rawTable, staticCols & {storeCol, tfCol}),
        renamed = Table.RenameColumns(selectedCols, {{storeCol, "Assortment"}, {tfCol, "TF"}}),
        addedStore = Table.AddColumn(renamed, "Store Name", each storeCol),
        filtered = Table.SelectRows(addedStore, each not (
            List.Contains({"0", "NO", null}, [Assortment]) and
            List.Contains({"0", "NO", null}, [TF])
        ))
    in
        filtered
)

For each valid pair:

  • Selects static columns + store + T&F.
  • Renames store to Assortment, T&F to TF.
  • Adds a new column Store Name with the name of the store column.
  • Filters out rows where both Assortment and TF are '0', 'NO', or null.

6. Combine All Transformed Tables

combined = Table.Combine(transformedTables)

Merges all the transformed tables into one long-format table.


7. Return the Final Table

in combined

Returns the final result.


:white_check_mark: Summary

This function is now:

  • Dynamic: Works with any number of static columns.
  • Safe: Avoids out-of-range errors.
  • Clean: Filters out irrelevant rows.
  • Reusable: Can be applied to different datasets with alternating store–T&F columns.

Hope this helps!

UnpivotStoresDynamic_pq_function.pq (1.7 KB)
Matrix_test.xlsx (254.8 KB)

Hi Ankit,

Thanks for the BI file, actually was looking for a Power Query solution.
I checked the BI file, the output seems wrong. If you see the screen shot above I have sent, store name with the assortment and T&F values should be like that.

Please see the screen shot, if you filter the first article number, the store name Dubai Mall - DM1 should have Yes and the value for T&F should be No. Same is the case for the other stores: DCC as Yes and T&F as No.
The problem I get is unpivoting all these columns.

Hi @chris786 - Do check the updated file and see if it works for your scenario.

Thanks
Ankit J
Store Solution1108.pbix (98.6 KB)

Yes, Ankit, it works, would you be able to do a step by step explanation of it. Would love to learn it, how it is done.

Thank you😊

Thanks Pranamg, yes the solution is correct, but the M is tough to understand.
Thanks anyway😊