Power Query: Concatenate All Rows - excel

I am looking to concatenate rows instead of columns. I can do this by pivoting and merging columns, but that process is not repeatable as the rows change (the column merging then hardcodes the values). How would I do a GroupBy function but concatenate rather than sum/count/etc?
NOTE: It's worth noting that the text must stay as a Text type. The prices are sometimes $275pp or something and I need to maintain the letters
A visual of what I am trying to do is below:

You just need to Group by Group and do some manipulations to add the quotation marks and brackets:
let
Source = Excel.CurrentWorkbook(){[Name="Table3"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Price", type text}, {"Group", Int64.Type}}),
#"Added Custom" = Table.AddColumn(#"Changed Type", "quotPrice", each """" & [Price] & """"),
#"Grouped Rows" = Table.Group(#"Added Custom", {"Group"}, {{"Grouped", each _, type table [Price=text, Group=number, quotPrice=text]}}),
#"Added Custom1" = Table.AddColumn(#"Grouped Rows", "Custom", each Table.Column([Grouped],"quotPrice")),
#"Extracted Values" = Table.TransformColumns(#"Added Custom1", {"Custom", each Text.Combine(List.Transform(_, Text.From), ", "), type text}),
#"Added Custom2" = Table.AddColumn(#"Extracted Values", "Text", each "[" & [Custom] & "]"),
#"Removed Columns" = Table.RemoveColumns(#"Added Custom2",{"Grouped", "Custom"})
in
#"Removed Columns"

The answer you're looking for is to combine a Table.Group() function with a Text.Combine() function.
This will work for you:
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WUjEyN1XSUTJUitUBcowNDBAcQyOQjBGEYwSWgXIMzZFkLMEyxkqxsQA=", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Price = _t, Group = _t]),
#"Grouped rows" = Table.Group(Source, {"Group"}, {{"Count", each "[""" & Text.Combine([Price], """, """) & """]", type text}})
in
#"Grouped rows"

Related

Excel Unpivot Multiple Columns to a Single Column

What I want to achieve:
As the title says, is there any way to convert my table structure? I've tried using Power Query but it didn't work. Any kind of help is greatly appreciated. Thanks!
So, I was trying to pivot type and type value, but it seems impossible if I maintain the current table structure since it would cause duplicates when I wanted to aggregate on type.
Should I remake the table structure or there is any way to get around this problem?
Thanks in advance!
In Power Query, the following is adaptable to any number of type/value column pairs.
Unpivot all except the ID column
Add a custom column to define if the unpivoted value is a Type or a Type Value
Add an Index column and then do an integer/divide by 2 so things will sort in the desired order
Pivot with no aggregation, using a custom function as the "built-in" function will error with multiple items.
Custom function to Pivot with No aggregation
Rename as noted in comments
//credit: Cam Wallace https://www.dingbatdata.com/2018/03/08/non-aggregate-pivot-with-multiple-rows-in-powerquery/
//Rename: fnPivotAll
(Source as table,
ColToPivot as text,
ColForValues as text)=>
let
PivotColNames = List.Buffer(List.Distinct(Table.Column(Source,ColToPivot))),
#"Pivoted Column" = Table.Pivot(Source, PivotColNames, ColToPivot, ColForValues, each _),
TableFromRecordOfLists = (rec as record, fieldnames as list) =>
let
PartialRecord = Record.SelectFields(rec,fieldnames),
RecordToList = Record.ToList(PartialRecord),
Table = Table.FromColumns(RecordToList,fieldnames)
in
Table,
#"Added Custom" = Table.AddColumn(#"Pivoted Column", "Values", each TableFromRecordOfLists(_,PivotColNames)),
#"Removed Other Columns" = Table.RemoveColumns(#"Added Custom",PivotColNames),
#"Expanded Values" = Table.ExpandTableColumn(#"Removed Other Columns", "Values", PivotColNames)
in
#"Expanded Values"
Regular Query
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{
{"ID", type text}, {"Type 1", type text}, {"Type 1 Value", Int64.Type}, {"Type 2", type text}, {"Type 2 Value", Int64.Type}}),
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Changed Type", {"ID"}, "Attribute", "Value"),
#"Added Custom" = Table.AddColumn(#"Unpivoted Other Columns", "Custom",
each if Text.EndsWith([Attribute],"Value") then "Value" else "Type"),
#"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"Attribute"}),
#"Added Index" = Table.AddIndexColumn(#"Removed Columns", "Index", 0, 1, Int64.Type),
#"Inserted Integer-Division" = Table.AddColumn(#"Added Index", "Integer-Division", each Number.IntegerDivide([Index], 2), Int64.Type),
#"Removed Columns1" = Table.RemoveColumns(#"Inserted Integer-Division",{"Index"}),
Pivot = fnPivotAll(#"Removed Columns1","Custom","Value"),
#"Removed Columns2" = Table.RemoveColumns(Pivot,{"Integer-Division"}),
#"Changed Type1" = Table.TransformColumnTypes(#"Removed Columns2",{{"Type", type text}, {"Value", Int64.Type}})
in
#"Changed Type1"
More generically to stack vertically in powerquery while keeping certain columns
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
base_columns=1, groupsof=2, //stack them
Combo = List.Transform(List.Split(List.Skip(Table.ColumnNames(Source),base_columns),groupsof), each List.FirstN(Table.ColumnNames(Source),base_columns) & _),
#"Added Custom" =List.Accumulate(Combo, #table({"Column1"}, {}),(state,current)=> state & Table.Skip(Table.DemoteHeaders(Table.SelectColumns(Source, current)),1)),
#"Rename"=Table.RenameColumns(#"Added Custom",List.Zip({Table.ColumnNames(#"Added Custom"),List.FirstN(Table.ColumnNames(Source),base_columns+groupsof)}))
in #"Rename"
What seems to be fastest method of those I've tested
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
leading=1, groupsof=2,
#"Added Custom" = Table.AddColumn(Source, "Custom", each List.Split( List.RemoveFirstN(Record.ToList( _),leading), groupsof) ),
#"Added Custom0" = Table.AddColumn(#"Added Custom", "Custom0", each Text.Combine(List.FirstN(Record.ToList(_),leading),"|")),
#"Removed Other Columns" = Table.SelectColumns(#"Added Custom0",{"Custom0", "Custom"}),
#"Expanded Custom" = Table.ExpandListColumn( #"Removed Other Columns", "Custom"),
#"Extracted Values" = Table.TransformColumns(#"Expanded Custom", {"Custom", each Text.Combine(List.Transform(_, Text.From), "|"), type text}),
#"Merged Columns" = Table.CombineColumns(#"Extracted Values",{"Custom0", "Custom"},Combiner.CombineTextByDelimiter("|", QuoteStyle.None),"Custom"),
#"Split Column by Delimiter" = Table.SplitColumn(#"Merged Columns", "Custom", Splitter.SplitTextByDelimiter("|", QuoteStyle.Csv), List.FirstN(Table.ColumnNames(Source),leading+groupsof))
in #"Split Column by Delimiter"
There is probably a better way, but if you first concat the 4 columns with specific unique delimiter to split on later in a custom column, you have a work-around in PQ:
let
Source = Excel.CurrentWorkbook(){[Name="Table2"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"ID", type text}, {"Type1", type text}, {"Type1 Val", Int64.Type}, {"Type2", type text}, {"Type2 Val", Int64.Type}}),
#"Added Custom" = Table.AddColumn(#"Changed Type", "Custom1", each [Type1]&"|"&Number.ToText([Type1 Val])&"$"&[Type2]&"|"&Number.ToText([Type2 Val])),
#"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"Type1", "Type1 Val", "Type2", "Type2 Val"}),
#"Split Column by Delimiter" = Table.ExpandListColumn(Table.TransformColumns(#"Removed Columns", {{"Custom1", Splitter.SplitTextByDelimiter("$", QuoteStyle.Csv), let itemType = (type nullable text) meta [Serialized.Text = true] in type {itemType}}}), "Custom1"),
#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Custom1", type text}}),
#"Split Column by Delimiter1" = Table.SplitColumn(#"Changed Type1", "Custom1", Splitter.SplitTextByEachDelimiter({"|"}, QuoteStyle.Csv, false), {"Custom1.1", "Custom1.2"}),
#"Changed Type2" = Table.TransformColumnTypes(#"Split Column by Delimiter1",{{"Custom1.1", type text}, {"Custom1.2", Int64.Type}})
in
#"Changed Type2"
Just in case you tagged 'Excel-Formula' and you have access to ms365:
Formula in H1:
=REDUCE({"ID","Type","Val"},ROW(A2:A5),LAMBDA(X,Y,VSTACK(X,INDEX(A:E,Y,{1,2,3}),INDEX(A:E,Y,{1,4,5}))))
Or formula:
=SORT(VSTACK(A2:C5,HSTACK(A2:A5,D2:E5)))

how to count occurrences of values in a specific column in excel power query

I want to calculate the running count of each value based on column SF ID. In Excel power query , I am trying to apply countif in the following table but i cant find this equation here.
I would like to get the same result in excel Power query. Can you please advise.
i've used to group the date like below but this isn't the result that i want.
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("bcy5CQAwDASwXVwHzm+eWUz2XyOQzuBWhTJJIFCWoEFizCx0R5JCi+pXgxW1rw5vhkA0w8RshoXVDBu7GQ5OUad7Hw==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Date = _t, #"SF ID" = _t]),
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Date", type date}, {"SF ID", Int64.Type}}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"SF ID"}, {{"Count", each _, type table [Date=nullable date, SF ID=nullable number]}}),
#"Added Custom" = Table.AddColumn(#"Grouped Rows", "Custom", each Table.AddIndexColumn([Count], "Index", 1)),
#"Removed Other Columns" = Table.SelectColumns(#"Added Custom",{"Custom"}),
#"Expanded Custom" = Table.ExpandTableColumn(#"Removed Other Columns", "Custom", {"Date", "SF ID", "Index"}, {"Date", "SF ID", "Index"})
in
#"Expanded Custom"
CALCULATE(
COUNTROWS(tbl)
,ALLEXCEPT(tbl,tbl[SF ID])
,tbl[Date]<=MAX(tbl[Date])
)
If I understood correctly, try this :
Select the two columns Date and SFID an make a groupby.
EDIT :
Open the Advanced Editor and put the code below :
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Date", type datetime}, {"SF ID", Int64.Type}}),
#"Sorted Rows" = Table.Sort(#"Changed Type",{{"SF ID", Order.Ascending}, {"Date", Order.Ascending}}),
#"Grouped Rows" = Table.Group(#"Sorted Rows", {"SF ID"}, {{"AllData", each _, type table [Date=nullable datetime, SFID=nullable number]}}),
#"Added Custom" = Table.AddColumn(#"Grouped Rows", "Status", each Table.AddIndexColumn([AllData], "Status", 1)),
#"Expanded Custom" = Table.ExpandTableColumn(#"Added Custom", "Status", {"Date", "Status"}, {"Date", "Status"}),
#"Removed Columns" = Table.RemoveColumns(#"Expanded Custom",{"AllData"}),
#"Reordered Columns" = Table.ReorderColumns(#"Removed Columns",{"Date", "SF ID", "Status"})
in
#"Reordered Columns"
Make sure that your table is named "Table1". Otherwise, you have to rename it.

How do I remove duplicates WITHIN a row using Power Query?

I have data that looks like this:
Wire
Point1
Point2
Point3
Point4
Point5
Point6
A
WP1
WP1
WP2
WP2
B
WP3
WP4
WP3
WP4
C
WP5
WP5
WP6
WP7
WP6
WP7
(note the varying lengths of each row, and the duplicates)
I would like to have the end result be:
Wire
Point1
Point2
Point3
A
WP1
WP2
B
WP3
WP4
C
WP5
WP6
WP7
Duplicates removed, and blank spaces removed.
This would be VERY similar to the =UNIQUE() function, but that is not available in power query.
It's a lot easier to work with columns, so I'd recommend unpivoting the Point columns, removing duplicates, and then putting it into the shape you want.
Here's a full query you can past into your Advanced Editor to look at each step more closely:
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WclTSUQoPMEQijYAkBIHYsTrRSk5gtjGYNIGzYWpMwGqcwWxTJNIMTJqjsGNjAQ==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Wire = _t, Point1 = _t, Point2 = _t, Point3 = _t, Point4 = _t, Point5 = _t, Point6 = _t]),
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Wire", type text}, {"Point1", type text}, {"Point2", type text}, {"Point3", type text}, {"Point4", type text}, {"Point5", type text}, {"Point6", type text}}),
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Changed Type", {"Wire"}, "Attribute", "Value"),
#"Filtered Rows" = Table.SelectRows(#"Unpivoted Other Columns", each ([Value] <> "")),
#"Removed Duplicates" = Table.Distinct(#"Filtered Rows", {"Wire", "Value"}),
#"Grouped Rows" = Table.Group(#"Removed Duplicates", {"Wire"}, {{"Point", each Text.Combine([Value],","), type text}}),
#"Split Column by Delimiter" = Table.SplitColumn(#"Grouped Rows", "Point", Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv), {"Point1", "Point2", "Point3"})
in
#"Split Column by Delimiter"
Unpivot
Group by Wire
Aggregate into sorted List of Unique Points
Calculate Max number of items in all the Lists to use in the later Column Splitter
Extract the List of points into semicolon separated string
Split into new columns
M Code
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source, List.Transform(Table.ColumnNames(Source), each {_, Text.Type})),
//Unpivot
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Changed Type", {"Wire"}, "Attribute", "Value"),
#"Removed Columns" = Table.RemoveColumns(#"Unpivoted Other Columns",{"Attribute"}),
//Group by Wire
//Aggregate by sorted, unique list of Points for each Wire
#"Grouped Rows" = Table.Group(#"Removed Columns", {"Wire"}, {
{"Point", each List.Sort(List.Distinct([Value]))}}),
//Calculate the Max unique Points for any Wire (for subsequent splitting
maxPoints = List.Max(List.Transform(#"Grouped Rows"[Point], each List.Count(_))),
//Extract the List values into a semicolon separated list
#"Extracted Values" = Table.TransformColumns(#"Grouped Rows",
{"Point", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
//Then split into new columns using the semicolon delimiter
#"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values", "Point",
Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv),maxPoints)
in
#"Split Column by Delimiter"

Arranging values in a specific order using power query

I wish to clean up a table I have been working on removing complex formulas and opting to sort my data using PQE.
Below is a made up example of the data I am working with.
I wish to have the output column alternate between material and sub values akin to headers and sub headers in a book.
M Code so far:
let
Source = Excel.CurrentWorkbook(){[Name="Table3"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Material ", type text}, {"Sub ", type text}, {"CAS", type text}}),
#"Trimmed Text" = Table.TransformColumns(#"Changed Type",{{"Material ", Text.Trim, type text}, {"Sub ", Text.Trim, type text}}),
#"Cleaned Text" = Table.TransformColumns(#"Trimmed Text",{{"Sub ", Text.Clean, type text}}),
#"Replaced Value" = Table.ReplaceValue(#"Cleaned Text","-","",Replacer.ReplaceText,{"Sub "}),
#"Merged Columns" = Table.CombineColumns(#"Replaced Value",{"Material ", "Sub "},Combiner.CombineTextByDelimiter(" ", QuoteStyle.None),"Merged"),
#"Split Column by Delimiter" = Table.ExpandListColumn(Table.TransformColumns(#"Merged Columns", {{"Merged", Splitter.SplitTextByDelimiter(" ", QuoteStyle.Csv), let itemType = (type nullable text) meta [Serialized.Text = true] in type {itemType}}}), "Merged"),
#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Merged", type text}})
in
#"Changed Type1"
The problem with this code is that it doesn't properly list the materials when combined and I also don't know how to get the CAS numbers to be correctly sorted.
If anyone has any thoughts how to achieve the desired output it would be appreciated.
You can get your output from your input using the same technique I showed you in your last, similar question.
create two lists of the columns; then List.Zip to combine them.
The trick is that for List 1, (eg output column 1), you may need to add the contents of the Material column at the top of the Sub column list (or replace the - if that's all there is; and, if that is the case, add a - at the start of the CAS list; so things will line up at the end.
M Code
let
Source = Excel.CurrentWorkbook(){[Name="Table13"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Material", type text}, {"Sub", type text}, {"CAS", type text}}),
//combine the columns
#"Added Custom" = Table.AddColumn(#"Changed Type", "Custom", each
let
L1 = if [Sub] = "-" then {[Material]}
else List.Combine({{[Material]},Text.Split([Sub],"#(lf)")}),
L2 = if [Sub] = "-" then {[CAS]}
else List.Combine({{"-"},Text.Split([CAS],"#(lf)")})
in
List.Zip({L1,L2})),
#"Removed Columns1" = Table.RemoveColumns(#"Added Custom",{"Material", "Sub", "CAS"}),
//split the combined columns
#"Expanded Custom" = Table.ExpandListColumn(#"Removed Columns1", "Custom"),
#"Extracted Values" = Table.TransformColumns(#"Expanded Custom",
{"Custom", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
#"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values",
"Custom", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {"Material", "CAS"})
in
#"Split Column by Delimiter"

Is there a way to separate a column into multiple columns using each text-based cell as a delimiter?

I am trying to build a function that will look through one column and split it into multiple columns using any text cell as the delimiter. Below is an example of what I mean by this.
I am looking for a function that will turn this:
into this:
I wish I could document what I have tried so far, but I don't even know where to begin or what to even search on Google to find an answer to this. I've also asked co-workers and nobody has a clue. Any help will be appreciated!
Edit: Made title more clear for others searching for a similar problem
It would easier if you had Office365. Now can do but it will slower for large dataset. As per my below screenshot first put below formula in C1 cell then drag across right to get fruits names.
=IFERROR(INDEX($A$1:$A$12,AGGREGATE(15,6,ROW($A$1:$A$12)/ISTEXT($A$1:$A$12),COLUMN(A$1))),"")
To get values use following formula to C2 cell then drag and down then right as needed.
=IFERROR(IF(D$1="",INDEX(INDIRECT("A"&MATCH(C$1,$A$1:$A$12,0)+1&":A"&LOOKUP(2,1/($A$1:$A$12<>""),ROW($A$1:$A$12))),ROW(1:1)),INDEX(INDIRECT("A" & MATCH(C$1,$A$1:$A$12,0)+1 & ":A" & MATCH(D$1,$A$1:$A$12,0)-1),ROW(1:1))),"")
Here's a powerquery script that will do it for you. Insert>add table. Then data>from table or range. Then to go to view>advanced editor and paste in this script. Then home>close and load.
enter code here
let
Source = Excel.CurrentWorkbook(){[Name="Table5"]}[Content],
#"Demoted Headers" = Table.DemoteHeaders(Source),
#"Duplicated Column" = Table.DuplicateColumn(#"Demoted Headers", "Column1", "Column1 - Copy"),
#"Changed Type" = Table.TransformColumnTypes(#"Duplicated Column",{{"Column1", Int64.Type}}),
#"Added Custom" = Table.AddColumn(#"Changed Type", "Custom", each try [Column1]/[Column1] otherwise ""),
#"Added Conditional Column" = Table.AddColumn(#"Added Custom", "Custom.1", each if [Custom] <> 1 then [#"Column1 - Copy"] else null),
#"Filled Down" = Table.FillDown(#"Added Conditional Column",{"Custom.1"}),
#"Filtered Rows" = Table.SelectRows(#"Filled Down", each ([Custom] = 1)),
#"Removed Columns" = Table.RemoveColumns(#"Filtered Rows",{"Custom", "Column1"}),
#"Changed Type1" = Table.TransformColumnTypes(#"Removed Columns",{{"Column1 - Copy", type text}}),
#"Renamed Columns" = Table.RenameColumns(#"Changed Type1",{{"Custom.1", "Fruit"}, {"Column1 - Copy", "Value"}}),
#"Reordered Columns" = Table.ReorderColumns(#"Renamed Columns",{"Fruit", "Value"}),
#"Transposed Table" = Table.Transpose(#"Reordered Columns"),
#"Transposed Table1" = Table.Transpose(#"Transposed Table"),
#"Grouped Rows" = Table.Group(#"Transposed Table1", {"Column1"}, {{"Count", each _, type table [Column1=text, Column2=text]}}),
#"Added Custom1" = Table.AddColumn(#"Grouped Rows", "Custom", each Table.AddIndexColumn([Count],"Index",1)),
#"Expanded Custom" = Table.ExpandTableColumn(#"Added Custom1", "Custom", {"Column1", "Column2", "Index"}, {"Column1.1", "Column2", "Index"}),
#"Removed Columns1" = Table.RemoveColumns(#"Expanded Custom",{"Count", "Column1.1"}),
#"Pivoted Column" = Table.Pivot(Table.TransformColumnTypes(#"Removed Columns1", {{"Index", type text}}, "en-US"), List.Distinct(Table.TransformColumnTypes(#"Removed Columns1", {{"Index", type text}}, "en-US")[Index]), "Index", "Column2"),
#"Transposed Table2" = Table.Transpose(#"Pivoted Column"),
#"Promoted Headers" = Table.PromoteHeaders(#"Transposed Table2", [PromoteAllScalars=true]),
#"Changed Type2" = Table.TransformColumnTypes(#"Promoted Headers",{{"Apples", Int64.Type}, {"Bananas", Int64.Type}, {"Oranges", Int64.Type}})
in
#"Changed Type2"
Here will be a little simpler formula without INDIRECT.
For fruits:
=IFERROR(INDEX($A:$A,AGGREGATE(15,6,ROW($A$1:$A$12)/ISTEXT($A$1:$A$12),COLUMN(A$1))),"")
for numbers:
=IFERROR(INDEX($A:$A,AGGREGATE(15,6,ROW($A$1:$A$13)/((ROW($A$1:$A$13)>LOOKUP(9^9,ROW($A$1:$A$13)/($A$1:$A$13=D$1)))*(ROW($A$1:$A$13)<LOOKUP(9^9,ROW($A$1:$A$13)/($A$1:$A$13=E$1)))),ROW(A1))),"")

Resources