Forum Discussion
How to Return Column Data Based on All Populated Cells in Row Range and Match to Other Worksheet
- Jun 29, 2018
Hi Anthony,
That could be done by Power Query. Here is the simple mockup to illustrate idea based on your initial pictures, for the real data queries will be bit more complex.
Here on the left are source tables and resulting one are on the right
First query generates list of roles for users, here it dynamically split on columns
let Source = Excel.CurrentWorkbook(){[Name="User"]}[Content], UnpivotJobs = Table.UnpivotOtherColumns(Source, {"User ID"}, "Attribute", "Value"), RemoveValues = Table.RemoveColumns(UnpivotJobs,{"Value"}), GroupRows = Table.Group(RemoveValues, {"User ID"}, {{"Role", each [Attribute]}}), MaxInList = Table.AddColumn(GroupRows, "ListMax", each List.Count([Role])), ElementNumber = List.Max(MaxInList[ListMax]), ListColumns = List.Transform({1..ElementNumber}, each "Role " & Number.ToText(_)), ExtractElements = Table.TransformColumns(GroupRows, {"Role", each Text.Combine(List.Transform(_, Text.From), ","), type text}), SplitElements = Table.SplitColumn(ExtractElements, "Role", Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv), ListColumns) in SplitElements
For the second table we first create connection query which maps roles on lists of classes
let Source = Excel.CurrentWorkbook(){[Name="Job"]}[Content], UnpivotClasses = Table.UnpivotOtherColumns(Source, {"Job"}, "Attribute", "Value"), RemoveValue = Table.RemoveColumns(UnpivotClasses,{"Value"}), GroupRows = Table.Group(RemoveValue, {"Job"}, {{"Class", each [Attribute]}}) in GroupRows
and here we take users with roles and merge with that query
let Source = Excel.CurrentWorkbook(){[Name="User"]}[Content], UnpivotJobs = Table.UnpivotOtherColumns(Source, {"User ID"}, "Job", "Value"), RemoveValues = Table.RemoveColumns(UnpivotJobs,{"Value"}), MergeWithClasses = Table.NestedJoin(RemoveValues,{"Job"},ClassesForRoles,
{"Job"},"Classes",JoinKind.LeftOuter), ExpandClasses = Table.ExpandTableColumn(MergeWithClasses, "Classes", {"Class"}, {"Class"}), ExtractClasses = Table.TransformColumns(ExpandClasses, {"Class",
each Text.Combine(List.Transform(_, Text.From), ", "), type text}), SortUsers = Table.Sort(ExtractClasses,{{"User ID", Order.Descending},
{"Job", Order.Ascending}}) in SortUsersHere classes are as lists, could be split dynamically same way as roles in first query. Sample file is attached.
I don't have the spreadsheet in front of me at the moment, but a good estimate would be about 60 jobs and 45 classes. I saw a similar situation to mine before and the gentleman who answered it used helping columns to solve for the first part (adding the jobs, column headers, to the row in consecutive cells). He used a complex index function to search if a cell contained a value and then added the column header data to the cell where the function was written. He did this for each value in the row. Unfortunately, my data is a bit different than that of the person he was helping. I don't know how to go about accomplishing the second part. In the first sheet, it has a user's data and the letter "X" to denote which job that person has. There are probably 60 or so columns for jobs. In the second sheet, the jobs are the row headers and the classes are the column headers. There are probably about 45 classes. Each job has about 5 classes or less.
Hi Anthony,
That could be done by Power Query. Here is the simple mockup to illustrate idea based on your initial pictures, for the real data queries will be bit more complex.
Here on the left are source tables and resulting one are on the right
First query generates list of roles for users, here it dynamically split on columns
let Source = Excel.CurrentWorkbook(){[Name="User"]}[Content], UnpivotJobs = Table.UnpivotOtherColumns(Source, {"User ID"}, "Attribute", "Value"), RemoveValues = Table.RemoveColumns(UnpivotJobs,{"Value"}), GroupRows = Table.Group(RemoveValues, {"User ID"}, {{"Role", each [Attribute]}}), MaxInList = Table.AddColumn(GroupRows, "ListMax", each List.Count([Role])), ElementNumber = List.Max(MaxInList[ListMax]), ListColumns = List.Transform({1..ElementNumber}, each "Role " & Number.ToText(_)), ExtractElements = Table.TransformColumns(GroupRows, {"Role", each Text.Combine(List.Transform(_, Text.From), ","), type text}), SplitElements = Table.SplitColumn(ExtractElements, "Role", Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv), ListColumns) in SplitElements
For the second table we first create connection query which maps roles on lists of classes
let Source = Excel.CurrentWorkbook(){[Name="Job"]}[Content], UnpivotClasses = Table.UnpivotOtherColumns(Source, {"Job"}, "Attribute", "Value"), RemoveValue = Table.RemoveColumns(UnpivotClasses,{"Value"}), GroupRows = Table.Group(RemoveValue, {"Job"}, {{"Class", each [Attribute]}}) in GroupRows
and here we take users with roles and merge with that query
let Source = Excel.CurrentWorkbook(){[Name="User"]}[Content], UnpivotJobs = Table.UnpivotOtherColumns(Source, {"User ID"}, "Job", "Value"), RemoveValues = Table.RemoveColumns(UnpivotJobs,{"Value"}), MergeWithClasses = Table.NestedJoin(RemoveValues,{"Job"},ClassesForRoles,
{"Job"},"Classes",JoinKind.LeftOuter), ExpandClasses = Table.ExpandTableColumn(MergeWithClasses, "Classes", {"Class"}, {"Class"}), ExtractClasses = Table.TransformColumns(ExpandClasses, {"Class",
each Text.Combine(List.Transform(_, Text.From), ", "), type text}), SortUsers = Table.Sort(ExtractClasses,{{"User ID", Order.Descending},
{"Job", Order.Ascending}}) in SortUsers
Here classes are as lists, could be split dynamically same way as roles in first query. Sample file is attached.
- Anthony SmithJul 02, 2018Brass ContributorMy job currently uses Microsoft Excel 2013, and I am unable to install the Power Query add-on. It looks as though I will not be able to do this.
- Anthony SmithJul 02, 2018Brass Contributor
Hi Sergei,
Thank you so much for the help. For this query, do both worksheets have to be in the same workbook?
- Matt MickleJul 01, 2018Bronze Contributor
Sergei-
Great example of how robust Power Query can be in a situation like the below described. Thanks for jumping in...I had thought of a few different ways to attack the issue using VBA, but I believe this to be a much simpler solution.
- SergeiBaklanJul 02, 2018Diamond Contributor
Matt, thank you. I believe that could be done with VBA and even with formulas, but PQ is naturally for tasks like this.
- Anthony SmithJul 02, 2018Brass ContributorIn order to replace all of the "X's" with column headers, I just used an IF statement and deleted all of the in between blank cells. I just need to figure out how to assign the classes based on roles using a formula.