Add Custom and Dynamic columns - excel

I have two tables and am trying to figure out how to create custom code to add dynamic columns with custom names that are based off of row values in another table. I then need to use the values of rows in Table 2 to not only create the column names but also fill the new dynamic Columns with a value from another column in Table 2. Hopefully my pictures below help
Table 1 has varying amount of rows depending on what the user input.
Table 2 has varying amount of rows depending on how many values the user inputs.
Table 1 Before
Col1
Col2
Col 3
stuff 1
stuff 2
stuff 3
stuff 4
stuff 5
stuff 6
.
.
.
.
.
.
Table 2
Name
Values
Name1
100
Name2
500
.
.
NameX
Y
Table 1 After
Col1
Col2
Col 3
"Column" & Name1
"Column"& Name2
...
"Column"& NameX
stuff 1
stuff 2
stuff 3
100
500
...
Y
stuff 4
stuff 5
stuff 6
100
500
...
Y
.
.
.
100
500
...
Y
.
.
.
100
500
...
Y
The "Column" & Name1 meaning I want to concatenate Column with the values in the Name column in Table 2.

You can make this dynamic by not referring to the absolute column names, but rather using the Table.ColumnNames function to return those names.
I did assume that the column names in Table 2 are fixed. If not, that code can be changed.
Read the code comments and examine the Applied Steps window to better understand the methods used. There are examples of setting the data type, and also re-naming columns without referring to a hard-coded column name.
M Code
let
//read in the two tables and set the data types
Source1 = Excel.CurrentWorkbook(){[Name="Table_2"]}[Content],
Table2 =Table.TransformColumnTypes(Source1,
{{"Name", type text},{"Values", type any}}),
Source = Excel.CurrentWorkbook(){[Name="Table_1_Before"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,
List.Transform(Table.ColumnNames(Source), each {_, type text})),
//create the extra columns by
//Transpose Table2
// Use first row as headers
xpose = Table.Transpose(Table2),
#"Promoted Headers" = Table.PromoteHeaders(xpose, [PromoteAllScalars=true]),
#"Changed Type1" = Table.TransformColumnTypes(#"Promoted Headers",
List.Transform(Table.ColumnNames(#"Promoted Headers"), each {_, type any})),
//rename the columns
renameNameCols = Table.RenameColumns(#"Changed Type1",
List.Zip(
{Table.ColumnNames(#"Changed Type1"),
List.Transform(Table.ColumnNames(#"Changed Type1"), each "Column " & _)})),
//Combine the tables
combine = Table.Combine({#"Changed Type",renameNameCols}),
//fill up the original table 2 columns and remove the blank Table 1 rows
#"Filled Up" = Table.FillUp(combine,Table.ColumnNames(renameNameCols)),
#"Filtered Rows" = Table.SelectRows(#"Filled Up", each ([Col1] <> null))
in
#"Filtered Rows"
Original Tables
Results
Note that I did NOT add logic to avoid prepending the ... with the word column, as shown in your desired output, but that is easily added if really needed

My version
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
custom = Table.FromColumns(Table.ToColumns(Source) &Table.ToColumns(Table2), List.Combine({Table.ColumnNames(Source),Table.ColumnNames(Table2)}) ),
#"Filled Down" = Table.FillDown(custom,Table.ColumnNames(Table2))
in #"Filled Down"

Related

How can I join two tables and keep both matched and unmatched values from the joining column?

I have two tables in Power Query that look like this:
Table A
ID
Col 2
1
A
2
B
3
C
Table B
ID
Col 2
1
2
3
4
D
5
E
Expected Outcome of joining tables (joining on ID column):
ID
Col 2
1
A
2
B
3
C
4
D
5
E
But when I perform a full outer join in Power Query (in order to keep unmatched rows of data), I get this:
ID
Col 2
1
A
2
B
3
C
D
E
I get the values from Table B Col 2, but I am missing the unmatched IDs from Table B.
How can I perform this kind of join in Power Query?
Use Full Outer Join in Power Query
To accomplish this task follow the steps:
Select some data in the first table,
From Data Tab --> Click on From Table/Range Under Get & Transform Data.
This opens the PQ Editor, and select the Close & Load To From Home Tab, and import as Only Create Connection.
Follow the steps 1 To 3 for the second table and import it as a connection as well.
Now, from Data Tab --> Get Data --> Click on Combine Queries & Select Merge.
On selecting the above, a new window opens which shows as below, select the id for both the tables and select the join kind as Full Outer
Once now the PQ Editor opens, click on Advance Editor from the Home Tab, remove anything that shows and paste the following M-Code
let
Source = Table.NestedJoin(Table14, {"ID"}, Table13, {"ID"}, "Table13", JoinKind.FullOuter),
#"Expanded Table13" = Table.ExpandTableColumn(Source, "Table13", {"ID", "Col 2"}, {"ID.1", "Col 2.1"}),
#"Merged Columns" = Table.CombineColumns(#"Expanded Table13",{"Col 2.1", "Col 2"},Combiner.CombineTextByDelimiter("", QuoteStyle.None),"Merged"),
#"Reordered Columns" = Table.ReorderColumns(#"Merged Columns",{"ID", "ID.1", "Merged"}),
#"Removed Columns" = Table.RemoveColumns(#"Reordered Columns",{"ID"})
in
#"Removed Columns"
Note kindly change the table names as with your existing ones as well, and import the data again to excel to get the desired output you are looking for.
Updated M-Code:
let
SourceOne = Excel.CurrentWorkbook(){[Name="TableOne"]}[Content],
SourceTwo = Excel.CurrentWorkbook(){[Name="TableTwo"]}[Content],
MergeTables = Table.NestedJoin(SourceOne, {"ID"}, SourceTwo, {"ID"}, "SourceTwo", JoinKind.FullOuter),
Expanded = Table.ExpandTableColumn(MergeTables, "SourceTwo", {"ID", "Col 2"}, {"ID.1", "Col 2.1"}),
MergedColumns = Table.CombineColumns(Expanded,{"Col 2.1", "Col 2"},Combiner.CombineTextByDelimiter("", QuoteStyle.None),"Merged"),
ReorderedColumns = Table.ReorderColumns(MergedColumns,{"ID", "ID.1", "Merged"}),
#"Removed Columns" = Table.RemoveColumns(ReorderedColumns,{"ID"})
in
#"Removed Columns"
With the present M-Code, you need to do only three things, convert both the ranges into a table and open a blank query and paste the above M-Code by removing the existing one.
Not sure if your data is oversimplified, but why not just append the two tables and remove the rows with null in Col2?
let Source = Table.SelectRows(Table.Combine({TableA, TableB}), each [Col 2] <> null) in Source

Hi everyone. Could anyone help me with this Power query problem

I have a table as indicated below where the Category column could have multiple categories each with its own scale which is shown in the Value column. I would like to create a new column that multiplies all the values where the item column does not equal 'scale' by the corresponding scale value that matches its category.
In the example below: if Category = A then multiply the value by 10
I've been stuck with this for a while and would truly appreciate any help.
Note - there are hundreds of categories and each one could have a different value. In the actual data, the category is a date and for each of those dates, there is a corresponding scale value that the remainder of the data that matches that date will need to be multiplied by.
Original Table
Item
Category
Value
Scale
A
10
Scale
B
5
Scale
C
2
Apples
A
100
Fruit
B
10
Car
C
15
Pear
A
20
Lemon
B
5
New Column added
Item
Category
Value
Calc_Val
Scale
A
10
null
Scale
B
5
null
Scale
C
2
null
Apples
A
100
1000
Fruit
B
10
50
Car
C
15
30
Pear
A
20
200
Lemon
B
5
25
Assuming your first table is named Table1, create a new table, Table2, via
let
Source = Table1,
#"Filtered Rows" = Table.SelectRows(Source, each ([Item] = "Scale"))
in
#"Filtered Rows"
Then create a third table, via
let
Source = Table.NestedJoin(
Table1,
{"Category"},
Table2,
{"Category"},
"Table2",
JoinKind.LeftOuter
),
#"Expanded Table2" = Table.ExpandTableColumn(Source, "Table2", {"Value"}, {"Table2.Value"}),
#"Added Custom" = Table.AddColumn(
#"Expanded Table2",
"Custom",
each if [Item] = "Scale" then null else [Value] * [Table2.Value]
)
in
#"Added Custom"
Have a lookup table with each category and the matching multiplication factor, then use the following formula:
=C2*INDEX(H$2:H$5,MATCH(A2,G$2:G$5,0))

Split comma delimited cell into multiple rows, keeping Original Rows as it is?

I have tried Excel Power Query option to Split Comma Seprated Values into Rows. It working Well.
But I want to Keep original row as it is.
Because Power Query Deletes Original Row and Generates New. I want to keep original row & Generate new rows below that. by comma delimiter.
How can we do this?
Expectation:
I have Data Rows like
1 - name - A,B
2 - name - A,B
It should Convert Like This.
1 - name - A,B
1 - name - A
1 - name - B
2 - name - A,B
2 - name - A
2 - name - B
Just add a custom column which contains a List consisting of Column 3 plus the Split of column 3 with the comma delimiter.
Then expand that column into rows
Custom Column:
{[Column3]} & Text.Split([Column3],",")
original data
M Code
let
//change Table name in next line to whatever your real source is named
Source = Excel.CurrentWorkbook(){[Name="Table12"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{
{"Column1", Int64.Type}, {"Column2", type text}, {"Column3", type text}}),
//create the list for the column
#"Added Custom" = Table.AddColumn(#"Changed Type", "Custom", each {[Column3]} & Text.Split([Column3],",")),
//remove old column 3 and expand the list
#"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"Column3"}),
#"Expanded Custom" = Table.ExpandListColumn(#"Removed Columns", "Custom")
in
#"Expanded Custom"
Results
Use 2 imports.
What Power Query passes across to Excel is the model, and this can contain more than one table or process.
Import your data to be split. Carry out your Split by Delimiter into Rows. This will give you a table which has your separated values - Table After Split.
Import your data again as Table Original.
Append Table After Split to Table After Split to produce Table Combined.
Drop Table Original and Table After Split as they are no longer needed and are wasting memory.
Now what you have remaining in your model is your combined table.
Ok got the Simple solution.
Just Split All Rows by Comma Delimiter 'using power query'. we get "After Split Rows"
Then Paste All Original Rows Above "After Split Rows"
Add the filter to numbers/id row & use "Sort smallest to largest"
and Done.
We get the Expected Result with the Original Rows & Split Rows Below them, as shown in above question.

Increment difference between cells

I'm trying to duplicate data in a sheet with increments of 12 between each cell from a sheet with 1 cell per row. Between the 12-incremented rows there's other data. This means I can't drag to extend the formula. Like this for customer numbers:
'SheetA'E3 = 'SheetB'Y2
'SheetA'E15 = 'SheetB'Y3
'SheetA'E27 = 'SheetB'Y4
..and so on. I've tried extending 12/24 cells at a time and copying but I can't make it work. Extending doesn't add +1 to one sheet, just +12/+24 to both. Doing this manually will take months. Can this be done without a VBA solution?
Any suggestions? I'm sorry if my terminology isn't on point here.
SheetA:
Try this (run as VBA code):
Sub test1()
For i01 = 0 To 100
Worksheets("SheetA").Cells(3 + 12 * i01, 5) = Worksheets("SheetB").Cells(2 + i01, 25)
Next i01
End Sub
Power Query, available in Windows Excel 2010+ and Office 365, can produce your SheetA given SheetB. Not sure about the effect of the variability you mention.
The query assumes that the correct parameters are listed as column headers in Sheet B. The column headers will get copied over as parameters to sheet A.
To use Power Query:
Select some cell in your Data Table
Data => Get&Transform => from Table/Range
When the PQ Editor opens: Home => Advanced Editor
Make note of the Table Name in Line 2
Paste the M Code below in place of what you see
Change the Table name in line 2 back to what was generated originally.
Read the comments and explore the Applied Steps to understand the algorithm
M Code
let
//Read in the data
//Change table name in next line to be the "real" table name
Source = Excel.CurrentWorkbook(){[Name="Table12"]}[Content],
//set data types based on first entry in the column
//will be independent of the column names
typeIt = Table.TransformColumnTypes(Source,
List.Transform(
Table.ColumnNames(Source), each
{_,Value.Type(Table.Column(Source,_){0})})
),
//UNpivot except for the c.number and c.name columns to create the Parameter and Level columns
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(typeIt, {"C. number", "C. name"}, "Parameter", "Level"),
//Group By C.Number
//Add the appropriate rows for each customer
//And a blank row to separate the customers
#"Grouped Rows" = Table.Group(#"Unpivoted Other Columns", {"C. number"}, {
{"All", each _, type table [C. number=nullable number, C. name=nullable text, Parameter=text, Level=any]},
{"custLabel", (t)=> Table.InsertRows(t,0,{
[C. number = null, C. name=null,Parameter = null, Level = null],
[C. number = null, C. name=null, Parameter = "Customer Number", Level="Customer Name"],
[C. number = null, C. name=null,Parameter = t[C. number]{0}, Level = t[C. name]{0}],
[C. number = null, C. name=null,Parameter = "Parameter", Level = "Level"]
})}
}),
//Remove the unneeded columns and expand the remaining table
#"Removed Columns" = Table.RemoveColumns(#"Grouped Rows",{"C. number", "All"}),
#"Expanded custLabel" = Table.ExpandTableColumn(#"Removed Columns", "custLabel", {"Parameter", "Level"}, {"Parameter", "Level"}),
//Remove the top blank row
//promote the new blank row to the Header location
#"Removed Top Rows" = Table.Skip(#"Expanded custLabel",1),
#"Promoted Headers" = Table.PromoteHeaders(#"Removed Top Rows", [PromoteAllScalars=true]),
//data type set to text since it will look better on the report
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Customer Number", type text}, {"Customer Name", type text}})
in
#"Changed Type"```
Data
Results
[ Indirect with row() ]
Assuming 'SheetA'E3 column is the target and 'SheetB'Y2 is the source data.
In SheetA!E3 cell put:
=INDIRECT("SheetB!Y"&( ( (row()-3) / 12) + 2)
Press Enter
Then select SheetA!E3 cell, copy. Then paste in SheetA!E24. The formula will update itself.
Idea :
Find the relation between the target cell row number and the source cell row number. [ b > a : 3 > 2 , 15 > 3, 27 > 4 ] leads to a = (b-3)/12 + 2 . (The math is sort of like figuring out a straight line equation from 3 coordinate.) Then use INDIRECT() to combine the calculated row number with the column address.

Concatenate power query columns that are offset from each other

The problem
I have a data set with two header rows. I've transposed the rows into columns to work with the headers before combining, but I need help with concatenation of column1 into column2, since past row 7 the columns are offset from one another by one row (see example image).
The goal
I've tried to use replace and concatenate myself with an index, but have been unable to achieve the desired end result where column2 row 8 is concatenated with column1 row 7, so that when I combine these columns and transpose again the headers will be correctly labeled (see example image).
Thank you for any suggestions and your time.
Example image:
Here's one way.
I start with your Problem table as a table named Table1:
Then I add an index. (Add Column > Index Column):
Then I add a custom column. (Add Column > Custom Column) With this setup:
(#"Added Index"{[Index]-1}[Column1] references the entry in Column1 at the position record row that is equal to the value in the Index column, minus 1.)
...to get this:
Then I replaced Errors in the new Custom column. (Right-click Custom column title > click Replace Errors > type null > click OK)
Then I select Column1 and Custom column and remove other columns. (Select Column 1 column title > hold Ctrl and click Custom column title > keep holding Ctrl and right click Custom column title > click Remove Other Columns)
Here's my M code:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Column1", type text}, {"Column2", type text}}),
#"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 0, 1),
#"Added Custom" = Table.AddColumn(#"Added Index", "Custom", each #"Added Index"{[Index]-1}[Column1]&"-"&[Column2]),
#"Replaced Errors" = Table.ReplaceErrorValues(#"Added Custom", {{"Custom", null}}),
#"Removed Other Columns" = Table.SelectColumns(#"Replaced Errors",{"Column1", "Custom"})
in
#"Removed Other Columns"
Another way.
Code:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
IndexedTable = Table.AddIndexColumn(Source, "Index", 0, 1),
Transform = Table.TransformRows(IndexedTable, (row)=>[Column1= row[Column1], Column2 = if row[Column1]=null then Text.Combine({IndexedTable{row[Index]-1}[Column1], "-",row[Column2]}) else row[Column2]]),
ToTable = Table.FromRecords(Transform)
in
ToTable
Brief explanation:
Source
Add index to address previous record
Use Table.TransformRows to analyze and transform each row to a record in this manner: Column1 taken from each row's column1 (row[Column1]), Column2 is generated from previous row using Text.Concatenate, IndexedTable{row[Index]-1}[Column1]. This yields value from previous row's Column1. Table.TransformRows returns list of records.
Transform list of records into the table.
This code will fail if 1st row contains null in [Column1]. If this is unacceptable, add another if-then-else.
Another way:
let
Source = Excel.CurrentWorkbook(){[Name="Table"]}[Content],
fillDown = Table.FillDown(Table.DuplicateColumn(Source, "Column1", "copy"),{"copy"}),
replace = Table.ReplaceValue(fillDown, each [Column2], each if [Column2] = null then null
else [copy]&"-"&[Column2], Replacer.ReplaceValue, {"Column2"})[[Column1],[Column2]]
in
replace

Resources