Forum Discussion
combine csv files with inconsistent columns and missing columns
- 1 year ago
Hi omrangassan, this will do the job, just change folder address in Source step.
Without CSV FileNames:
let Source = Folder.Files("c:\Users\YourUser\finbox\"), ColNames = List.Buffer(Text.Split("a263ff10 href,d6d4b13f,f397a0c8,b343055e href,b343055e,b343055e href (2),b343055e (2),b343055e href (3),b343055e (3),b343055e href (4),b343055e (4),b343055e href (5),b343055e (5),b343055e href (6),b343055e (6),b343055e href (7),b343055e (7),b343055e href (8),b343055e (8),b343055e href (9),b343055e (9),b343055e href (10),b343055e (10),b343055e href (11),b343055e (11),b343055e href (12),b343055e (12),b343055e href (13),b343055e (13),b343055e href (14),b343055e (14),b343055e href (15),b343055e (15),b343055e href (16),b343055e (16),b343055e href (17),b343055e (17),b343055e href (18),b343055e (18),b343055e href (19),b343055e (19),b343055e href (20),b343055e (20),b343055e href (21),b343055e (21),b343055e href (22),b343055e (22),b343055e href (23),b343055e (23),b343055e href (24),b343055e (24),b343055e href (25)", ",")), Combined = Table.Combine(Table.AddColumn(Source, "T", each Table.SelectColumns(Table.PromoteHeaders(Csv.Document([Content],[Delimiter=",", Columns=52, Encoding=1250, QuoteStyle=QuoteStyle.None])), ColNames, MissingField.UseNull))[T]) in CombinedIf you want to preserve CSV FileNames, use this query:
let Source = Folder.Files("c:\Users\YourUser\finbox\"), ColNames = List.Buffer(Text.Split("a263ff10 href,d6d4b13f,f397a0c8,b343055e href,b343055e,b343055e href (2),b343055e (2),b343055e href (3),b343055e (3),b343055e href (4),b343055e (4),b343055e href (5),b343055e (5),b343055e href (6),b343055e (6),b343055e href (7),b343055e (7),b343055e href (8),b343055e (8),b343055e href (9),b343055e (9),b343055e href (10),b343055e (10),b343055e href (11),b343055e (11),b343055e href (12),b343055e (12),b343055e href (13),b343055e (13),b343055e href (14),b343055e (14),b343055e href (15),b343055e (15),b343055e href (16),b343055e (16),b343055e href (17),b343055e (17),b343055e href (18),b343055e (18),b343055e href (19),b343055e (19),b343055e href (20),b343055e (20),b343055e href (21),b343055e (21),b343055e href (22),b343055e (22),b343055e href (23),b343055e (23),b343055e href (24),b343055e (24),b343055e href (25)", ",")), Combined = Table.Combine(Table.AddColumn(Source, "T", each Table.SelectColumns(Table.AddColumn(Table.PromoteHeaders(Csv.Document([Content],[Delimiter=",", Columns=52, Encoding=1250, QuoteStyle=QuoteStyle.None])), "Name", (x)=> [Name]), {"Name"} & ColNames, MissingField.UseNull))[T]) in CombinedCSV files and their missing columns:
finbox-2025-08-14 (43).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (44).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (45).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (46).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (47).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (48).csv b343055e (4), b343055e (16), b343055e (17) finbox-2025-08-14 (49).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (7), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (50).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (51).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (52).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (53).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (7), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (54).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (55).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (56).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (57).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (58).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (7), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (59).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (7), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (60).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (6), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (61).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (7), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (62).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (5), b343055e (6), b343055e (7), b343055e (8), b343055e (9), b343055e (10), b343055e (12), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (20), b343055e (21), b343055e (24) finbox-2025-08-14 (63).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (64).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (65).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (66).csv b343055e, b343055e (2), b343055e (3), b343055e (4), b343055e (8), b343055e (9), b343055e (13), b343055e (14), b343055e (15), b343055e (16), b343055e (17), b343055e (18), b343055e (19), b343055e (21), b343055e (24) finbox-2025-08-14 (84).csv b343055e (16)
Here is some M to parse the csvs, promote headers, then combine them all. This relies on somehow connecting to your source directory of csvs.
E.g., I downloaded your csvs to a local folder and connected via Folder.Files("<folder path>"). The actual folder/directory connection you use will depend on where your csv are in real life. E.g. if in SharePoint you could use SharePoint.Files, SharePoint.Content, or the SharePoint api.
let
Source = FolderConnection,
ParseCsvBinary = Table.TransformRows(Source, each Table.PromoteHeaders(Csv.Document([Content]))),
CombineCsvTables = Table.Combine(ParseCsvBinary)
in
CombineCsvTables
So, transformation outputs look like:
Source
ParseCsvBinary
CombineCsvTables
- MarkLaf1 year agoSuper User
After looking a little more at the output of the simple CSV combination above, I think I better understand your original request.
I think what you are aiming for is to pivot all your field/value column pairs, where the field is in the href column and value is in the corresponding non-href column. E.g. [b343055e href] is the "total_rev_trend_score" field (removing extra finbox path text) and [b343055e] is a percentage value like "85.7%"
A normal pivot won't work since you have many field/value columns stacked horizontally.
Here is M code building off above that does this full transformation.
let Source = FolderConnection, // Combine all csvs as is ParseCsvBinary = Table.TransformRows(Source, each Table.PromoteHeaders(Csv.Document([Content]))), CombineCsvTables = Table.Buffer( Table.Combine(ParseCsvBinary) ), // Perform pivot on every field,value column pair FixRows = Table.TransformRows( CombineCsvTables, each [ all = _, first3rec = Record.SelectFields( all, List.FirstN( Record.FieldNames(all), 3 ) ), fieldvals = Record.ToList( Record.SelectFields( all, List.RemoveFirstN( Record.FieldNames(all), 3 ) ) ), fields = List.Transform( List.Alternate( fieldvals, 1, 1, 1 ), each Text.AfterDelimiter(_,"/",{0,Occurrence.Last}) ), vals = List.ReplaceMatchingItems( List.Alternate( fieldvals, 1, 1 ), {{"-",null},{"",null}} ) , fixedfieldvalrec = Record.FromTable( Table.FromColumns( {fields,vals}, type table [Name=text,Value=text] ) ), finalrow = first3rec & fixedfieldvalrec ][finalrow] ), DynRowType = [ firstrec = List.First( FixRows ), fields = Record.FieldNames( firstrec ), fieldcount = Record.FieldCount(firstrec), typerecs = List.Repeat( { [Type=type nullable text,Optional=false] }, fieldcount ), rectypeval = Record.FromList(typerecs,fields), rectype = Type.ForRecord(rectypeval,false) ][rectype], RowsToTable = Table.FromRecords( FixRows, type table DynRowType ), // Transform all columns to proper type SimpleTypeConvert = Table.TransformColumnTypes( RowsToTable, { {"total_rev_trend_score", Percentage.Type}, {"eps_trend_score", Percentage.Type}, {"fcf_levered_trend_score", Percentage.Type}, {"gp_trend_score", Percentage.Type}, {"fin_health_growth_score", type number}, {"fin_health_profit_score", type number}, {"fin_health_cash_flow_score", type number}, {"total_rev_cagr_3y", Percentage.Type}, {"total_rev_cagr_5y", Percentage.Type}, {"total_rev_growth", Percentage.Type}, {"fcf_to_ni", Percentage.Type}, {"fcf_levered_share", Currency.Type}, {"fcf_levered_growth", Percentage.Type}, {"fcf_levered_cagr_3y", Percentage.Type}, {"fcf_levered_cagr_5y", Percentage.Type}, {"fcf_yield_ltm", Percentage.Type}, {"fcf_yield_avg_5y", Percentage.Type}, {"asset_price_return_1y", Percentage.Type}, {"first_trade_date", type date} } ), funcNumberTBMK = Value.ReplaceType( (x as nullable text) as nullable number => if x = null then null else [ parts = Text.Split( x, " " ), numpart = Number.From(parts{0}), tailpart = List.Skip( parts ), tailpartfix = List.First( List.ReplaceMatchingItems( tailpart, {{"T",1e12},{"B",1e9},{"M",1e6},{"K",1e3}} ) ), final = numpart * (tailpartfix ?? 1 ) ][final], type function (x as text) as nullable Currency.Type ), funcNumberX = (x as nullable text) as nullable number => if x = null then null else Number.FromText( Text.Remove(x, "x") ), funcOutputType = (x as function) as type => Type.FunctionReturn( Value.Type(x) ), ComplexTypeConvert = Table.TransformColumns( SimpleTypeConvert, List.Transform( {"marketcap","total_rev","fcf_levered","ni_company"}, each {_,funcNumberTBMK, funcOutputType(funcNumberTBMK) } ) & List.Transform( {"price_to_book"}, each {_,funcNumberX, funcOutputType(funcNumberX) } ) ) in ComplexTypeConvertOutput:
- omrangassan1 year agoNew Member
I tried to do this but it gave me this error :
Expression.Error: The import FolderConnection matches no exports. Did you miss a module reference?I have no experience in coding, i just copy/paste to advanced editor.
- MarkLaf1 year agoSuper User
That is referring to the
Source = FolderConnectionline at the top. The text FolderConnection should be replaced with whatever query you already have set up that connects to your folder of csvs. As long as that query has your binaries in a column called Content, the code I shared should work.
If you share more about where your csvs are sitting (SharePoint? local folder? etc), then I could provide some more guidance.
- omrangassan1 year agoNew Member
thanks bro, but how you did step 3 in details to match all columns from all cvs files together ?
- MarkLaf1 year agoSuper User
When you combine tables (same as Append action through UI), PQ will automatically match the column names. We promoted first row as headers in previous step, so everything is matching by the first row of data in the csvs.