I'm new to M and would like to create an "if, then, else statement" based on values inside a list.
Basically I have 4 lists:
let
FoodCompanies = {"Nestlé", "Pepsico", "Unilever"},
ClothingCompanies = {"Nike", "Ralph Lauren", "Old Navy"},
TechCompanies = {"Apple", "Samsung", "IBM"},
AllCompanies = {FoodCompanies,ClothingCompanies,TechCompanies}
Now I want to create a conditional column that checks for another column (tag) if one of the values is present and based on that makes a calculation.
| ItemId| DateOfSale | tag |
| 001 | 01/01/1980 | Nestlé |
| 002 | 01/01/1980 | Nike, Apple |
| 003 | 01/01/1980 | Unilever, Old Navy, IBM |
| 004 | 01/01/1980 | Samsung |
So ... I start like this:
#"Added Conditional Column" = Table.AddColumn(#"Renamed Columns3", "type", each
Single values
if [tag] = "" then "Empty tag"
else if [tag] = "Nestlé" then "Nestlé"
else if [tag] = "Nike" then "Nike"
...
Multiple values
It's for the multiple values I don't know how to create the logic
If tag contains more then 1 value from FoodCompanies but not from ClosthingCompanies or Techcompanies I want it to be "FoodCompanies"
If tag contains more then 1 value from ClothingCompanies but not from FoodCompanies or Techcompanies I want it to be "ClothingCompanies"
If tag contains 2 values from AllCompanies it should be "MixedCompanies"
if tag contains all values from AllCompanies it should be "AllofThem"
...
Anyone can help me on the way? I would do it like
else if List.Count(FoodCompanies) > 1 and ( List.Count(ClothingCompanies) < 1 or List.Count(Techcompanies) < 1) then "FoodCompanies"
but how do I evaluate it against the tag value?
Here's one approach, which converts your list of companies to a table, matches the tag values, filters the results, then determines the output:
#"Renamed Columns3" = //your previous step here
fnMatchingList = (MyList) =>
let
AllLists = #table(type table [#"ListName"=text, #"ListValues"=list],
{{"FoodCompanies",{"Nestlé", "Pepsico", "Unilever"}},
{"ClothingCompanies", {"Nike", "Ralph Lauren", "Old Navy"}},
{"TechCompanies",{"Apple", "Samsung", "IBM"}}}),
FullList = Table.ExpandListColumn(AllLists, "ListValues"),
Match = Table.AddColumn(FullList, "Match", each List.Contains(MyList,[ListValues])),
Filtered = Table.SelectRows(Match, each ([Match] = true)),
Output = if Table.RowCount(Filtered) = 1 then Filtered{0}[ListValues] else
if List.Distinct(Filtered[ListName]) = List.Distinct(FullList[ListName]) then "AllCompanies" else
Text.Combine(List.Distinct(Filtered[ListName]),", ")
in
Output,
#"Added Matching List" = Table.AddColumn(#"Previous Step", "taglist", each if [tag] = null or [tag] = "" then "(Empty Tag)" else fnMatchingList(Text.Split([tag],", ")))
Edit: to aid understanding, here's a standalone query which you can step through, to see what the function is actually doing:
let
MyList = {"Pepsico", "Nike"},
AllLists = #table(type table [#"ListName"=text, #"ListValues"=list],
{{"FoodCompanies",{"Nestlé", "Pepsico", "Unilever"}},
{"ClothingCompanies", {"Nike", "Ralph Lauren", "Old Navy"}},
{"TechCompanies",{"Apple", "Samsung", "IBM"}}}),
FullList = Table.ExpandListColumn(AllLists, "ListValues"),
Match = Table.AddColumn(FullList, "Match", each List.Contains(MyList,[ListValues])),
Filtered = Table.SelectRows(Match, each ([Match] = true)),
Output = if Table.RowCount(Filtered) = 1 then Filtered{0}[ListValues] else
if List.Distinct(Filtered[ListName]) = List.Distinct(FullList[ListName]) then "AllCompanies" else
Text.Combine(List.Distinct(Filtered[ListName]),", ")
in
Output
Related
i have an excel workbook with 3 worksheets. each of the worksheet contains hidden and unhidden rows. i’ve managed to combine all 3 worksheets into one single worksheet via power query. however, the worksheet generated shows all the hidden and unhidden rows. is there anyways i could just generate the worksheet with just the unhidden rows?
i’ve read about using a helping column and applying a filter on it but i am unsure of how to do that as well. any new suggestions/walkthrough is greatly appreciated
You can do this in Power Query.
You can unzip the XLSX file to XML tables, and examine the row attributes - then get the sheet data, and merge the hidden attribute with the row number.
Pass this function the file name (including path) and sheet name, and it will return the sheet contents, with an added "Row hidden" column:
//fnGetRowHiddenStatus
(MyFileName as text, MySheetName as text) =>
let
fnUnzip = (ZIPFile, Position, FileToExtract, DataSoFar) =>
let
MyBinaryFormat = try BinaryFormat.Record([DataToSkip=BinaryFormat.Binary(Position),
MiscHeader=BinaryFormat.Binary(18),
FileSize=BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger32, ByteOrder.LittleEndian),
UnCompressedFileSize=BinaryFormat.Binary(4),
FileNameLen=BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger16, ByteOrder.LittleEndian),
ExtrasLen=BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger16, ByteOrder.LittleEndian),
TheRest=BinaryFormat.Binary()]) otherwise null,
MyCompressedFileSize = try MyBinaryFormat(ZIPFile)[FileSize]+1 otherwise null,
MyFileNameLen = try MyBinaryFormat(ZIPFile)[FileNameLen] otherwise null,
MyExtrasLen = try MyBinaryFormat(ZIPFile)[ExtrasLen] otherwise null,
MyBinaryFormat2 = try BinaryFormat.Record([DataToSkip=BinaryFormat.Binary(Position), Header=BinaryFormat.Binary(30), Filename=BinaryFormat.Text(MyFileNameLen), Extras=BinaryFormat.Binary(MyExtrasLen), Data=BinaryFormat.Binary(MyCompressedFileSize), TheRest=BinaryFormat.Binary()]) otherwise null,
MyFileName = try MyBinaryFormat2(ZIPFile)[Filename] otherwise null,
GetDataToDecompress = try MyBinaryFormat2(ZIPFile)[Data] otherwise null,
DecompressData = try Binary.Decompress(GetDataToDecompress, Compression.Deflate) otherwise null,
NewPosition = try Position + 30 + MyFileNameLen + MyExtrasLen + MyCompressedFileSize - 1 otherwise null,
AsATable = Table.FromRecords({[Filename = MyFileName, Content=DecompressData]}),
#"Appended Query" = if DecompressData = null then DataSoFar else if (MyFileName = FileToExtract) then AsATable else
if (FileToExtract = "") and Position <> 0 then Table.Combine({DataSoFar, AsATable})
else AsATable
in
if (MyFileName = FileToExtract) or (#"Appended Query" = DataSoFar) then
#"Appended Query"
else
#fnUnzip(ZIPFile, NewPosition, FileToExtract, #"Appended Query"),
Unzipped = fnUnzip(File.Contents(MyFileName), 0, "", null),
WorkbookXML = Xml.Tables(Table.SelectRows(Unzipped, each Text.Contains([Filename],"xl/workbook"))[Content]{0}),
WorkbookData = Table.SelectRows(WorkbookXML, each [Name] = "sheets"){0}[Table]{0}[Table],
SheetIDs = Table.ExpandTableColumn(WorkbookData, "http://schemas.openxmlformats.org/officeDocument/2006/relationships", {"Attribute:id"}, {"Attribute:id"}),
SheetID = Text.Replace(Table.SelectRows(SheetIDs, each ([#"Attribute:name"] = MySheetName))[#"Attribute:id"]{0},"rId","sheet"),
SheetXML = Xml.Tables(Table.SelectRows(Unzipped, each Text.Contains([Filename], "worksheets/" & SheetID))[Content]{0}),
SheetData = Table.SelectRows(SheetXML, each [Name]="sheetData"){0}[Table]{0}[Table],
Renamed = Table.RenameColumns(SheetData,{{"Attribute:r", "Row"}}),
#"Row Data" = Table.SelectColumns(Renamed,{"Row", "Attribute:hidden"}, MissingField.Ignore),
#"Changed Type" = Table.TransformColumnTypes(#"Row Data",{{"Row", Int64.Type}}),
#"Row Status" = Table.AddColumn(#"Changed Type", "Hidden", each try if [#"Attribute:hidden"] = "1" then true else false otherwise false, type logical),
Workbook = Excel.Workbook(File.Contents(MyFileName)),
Worksheet = Table.SelectRows(Workbook, each ([Name] = MySheetName))[Data]{0},
#"Added Row" = Table.AddIndexColumn(Worksheet, "Row", 1, 1, Int64.Type),
#"Merged Row Status" = Table.NestedJoin(#"Added Row", {"Row"}, #"Row Status", {"Row"}, "Row Status", JoinKind.LeftOuter),
#"Expanded Row Status" = Table.ExpandTableColumn(#"Merged Row Status", "Row Status", {"Hidden"}, {"Row hidden"}),
#"Removed Columns" = Table.RemoveColumns(#"Expanded Row Status",{"Row"})
in
#"Removed Columns"
Example:
let
Source = fnGetRowHiddenStatus("C:\Temp\rowhide.xlsx", "Sheet1")
in
Source
source link
Hello guys, so i have a function ("flecheD"),
(ColChild,ColParent,ParentActuel,source)=>
let
mylist=Table.Column(Table.SelectRows(source,each Record.Field(_,ColParent)=ParentActuel),ColChild),
resultat=Text.Combine(mylist)
in
Text.Trim(
if resultat ="" then "" else # resultat &"|" & # flecheD(ColChild,ColParent,resultat,source),"|")
which loops through 2 columns (Parent,Child) to get all children of the main parent (output->Children column). The problem is that when the function is confronted with several children, the resultat variable no longer has a single letter/child but several, which blocks the function from looking for the other sub-children.
In order to solve this, I tried to create a custom function "SubChilldren" with List.Generate()
(children as text, ColChild,ColParent,source)=>
let
i = 1,
length = Text.Length(children),
subchildren = List.Generate( ()=>#flecheD(ColChild,ColParent,Text.At(children,i-1),source), i<=length, i+1 )
in
Text.Combine(subchildren)
which when coupled with my initial function
(ColChild,ColParent,ParentActuel,source)=>
let
mylist=Table.Column(Table.SelectRows(source,each Record.Field(_,ColParent)=ParentActuel),ColChild),
resultat=Text.Combine(mylist)
in
Text.Trim(
if resultat ="" then "" else if Text.Length(resultat) = 1 then # resultat &"|" & # flecheD(ColChild,ColParent,resultat,source)
else #resultat &"|"& SubChildren(resultat,ColChild,ColParent,source),"|")
should normally get the sub-children of each children. However, it doesnt work . Could you please help me . Thx
I thought this was a fun way, but you could write a recursive function as well. I have it hard coded to 4 levels of children deep
(not sure how in your source data D child can have two parents, c and J, but whatever)
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Grouped Rows" = Table.Group(Source, {"Parent"}, {{"data", each List.RemoveNulls(_[Child]), type list}}),
Parent_List = List.Buffer(#"Grouped Rows"[Parent] ),
Child_List = List.Buffer(#"Grouped Rows"[data] ),
Process = (n as list) as list =>
let children = List.Transform(List.Transform(n, each Text.ToList(_)), each Text.Combine( List.Distinct(List.Combine(List.Transform(_, each try Child_List{List.PositionOf( Parent_List, _ )} otherwise null))))) in children,
Level1=Process(Source[Parent]),
Level2=Process(Level1),
Level3=Process(Level2),
Level4=Process(Level3),
Final=List.Transform(List.Positions(Level1),each Level1{_}&"|"&Level2{_}&"|"&Level3{_}&"|"&Level4{_}&"|"),
#"Replaced Value" = Table.ReplaceValue(Table.FromList(Final),"||","",Replacer.ReplaceText,{"Column1"}),
custom1 = Table.ToColumns(Source) & Table.ToColumns(#"Replaced Value"),
custom2 = Table.FromColumns(custom1,Table.ColumnNames(Source) & {"Children"})
in custom2
edited to be generic so it can take text as well as numerical inputs
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Parent", type text}, {"Child", type text}}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"Parent"}, {{"data", each List.Transform(List.RemoveNulls(_[Child]), each Text.From(_)), type list}}),
Parent_List = List.Buffer(List.Transform(#"Grouped Rows"[Parent], each Text.From(_))),
Child_List = List.Buffer(#"Grouped Rows"[data]),
Process = (n as list) as list =>let children = List.Transform(List.Transform(n, each Text.Split(_,",") ) , each try Text.Combine(List.Distinct(List.Combine(List.Transform(_, each try Child_List{List.PositionOf( Parent_List, _ )} otherwise ""))),"," ) otherwise "") in children,
Level1=Process(#"Changed Type"[Parent]),
Level2=Process(Level1),
Level3=Process(Level2),
Level4=Process(Level3),
Final=List.Transform(List.Positions(Level1),each Level1{_}&"|"&Level2{_}&"|"&Level3{_}&"|"&Level4{_}&"|"),
#"Replaced Value" = Table.ReplaceValue(Table.FromColumns({Final}),"||","",Replacer.ReplaceText,{"Column1"}),
custom1 = Table.ToColumns(#"Changed Type") & Table.ToColumns(#"Replaced Value"),
custom2 = Table.FromColumns(custom1,Table.ColumnNames(#"Changed Type") & {"Children"})
in custom2
I have n application made in Powershell that reads result from a database and spool the data into an Excel table. When the numbers are integer i don't have any problem, but when I'm trying to spool a decimal the next error appears: "The number in this cell is formatted as text or preceded by an apostrophe".
I've tried the next after the data insert:
$range1 = "F2:G20"
$range2 = "K2:L20"
$WorkSheet.Columns.item('F').NumberFormat = "#.##0,00"
$WorkSheet.Columns.item('G').NumberFormat = "#.##0,00"
$range = $WorkSheet.Range($range1).Copy()
$x = $WorkSheet.Range($range2).Select()
$WorkSheet.UsedRange.PasteSpecial(-4163,-4142)
But no success... There is another way? I want those 2 columns F and G to be formated as Number....
As well I've tried as well :
$WorkSheet.Columns.item('F').NumberFormat = "#.##0,00"
$WorkSheet.Columns.item('G').NumberFormat = "#.##0,00"
$Tr = $WorkSheet.Range('K2','L20')
$WorkSheet.Range('F2','G20').Copy()
$Tr.Select()
$Tr.PasteSpecial(-4163)
The values are still pasting as text and not as a decimal number...
Very important : I'm trying to paste the values in a Excel Table.
OK, after too much time i found a solution, better call it work arround:
In your Excel workbook create a table with the same name of the column of you SQL table, then insert some dummy values manually.
Get your data information from the DB in this way:
$dt1 = SQLCommand -File_Path $QueryReplacedFullPath -Server "tnsdw" -Name "TMS_SalesDW" -Out
$dt_table1 = $dt1.Tables[0]
$WorkSheet = $Workbook.worksheets | where-object {$_.Name -eq $SheetName}
$WorkSheet.Activate()
Delete the dummy data inserted manually before wit the next code:
$StartRow = 2
$FinalRow = 200
$null = $WorkSheet.Range("A$($StartRow):A$($FinalRow)")
$WorkSheet.usedrange.offset($StartRow,0).specialcells(12).Entirerow.Delete()
Insert the data in your Excel table using this command:
for ([Int]$m = 0; $m -lt $dt_table1.Rows.Count; $m++)
{
for ([Int]$r = 0; $r -lt $dt_table1.Columns.Count; $r++)
{
$incolumn = $r + 1;
$inrow = $inheaderlenght + 2 + $m;
if($incolumn -gt 4)
{
$Workbook.ActiveSheet.Cells.Item($inrow, $incolumn) = [System.Convert]::ToDecimal($dt_table1.Rows[$m].ItemArray[$r])
}
else
{
$Workbook.ActiveSheet.Cells.Item($inrow, $incolumn) = $dt_table1.Rows[$m].ItemArray[$r].ToString()
}
}
}
Note that i convert the values to decimal only in the columns that i want to be decimal.
I want to remove any text between "( )" including the "( )".
There are many difference instances where I can't simply find and replace.
Small example: ABC (1)
EFG (2)
XYZ (1, 2)
I wish to display
ABC
EFG
XYZ
Found this post, but the code for the function is no longer visible(at least on all the browsers I've tried). https://www.thebiccountant.com/2019/07/15/text-removebetweendelimiters-function-for-power-bi-and-power-query/
I copied the code from one of the comments and it seems to work fine, however when I invoke the function on the column I get all errors with the following: "Expression.Error: The specified index parameter is invalid.
Details:
List"
Does anyone have the code from the author? Or know what I'm doing wrong?
Here is the code from the new custom column after I run the function:
Table.AddColumn(#"Changed Type1", "N", each Query1([#"NEC(s)"], "(", ")", 1, null))
Thanks
Here's a different solution that uses recursion.
(txt as text) =>
[
fnRemoveFirstTag = (DELIM as text)=>
let
OpeningTag = Text.PositionOf(DELIM,"("),
ClosingTag = Text.PositionOf(DELIM,")"),
Output =
if OpeningTag = -1
then DELIM
else Text.RemoveRange(DELIM,OpeningTag,ClosingTag-OpeningTag+1)
in
Output,
fnRemoveDELIM = (y as text)=>
if fnRemoveFirstTag(y) = y
then y
else #fnRemoveDELIM(fnRemoveFirstTag(y)),
Output = #fnRemoveDELIM(txt)
][Output]
It works on your sample data, and should also work if there is more than one set of parentheses delimited substrings in your string.
Copied shamelessly and modified minimally from Power Query: remove all text between delimiters
Is there text to the right of the )?
If not, just split column on custom delimiter ( leftmost, then remove the 2nd column
= Table.SplitColumn(Source, "Column1", Splitter.SplitTextByEachDelimiter({"("}, QuoteStyle.Csv, false), {"Column1.1", "Column1.2"})
OR transform the column to remove anything after the initial (
= Table.TransformColumns(Source,{{"Column1", each Text.Start(_,Text.PositionOf(_,"(")), type text}})
If text to the right of the ), try
= Table.TransformColumns(Source,{{"Column1", each Text.Start(,Text.PositionOf(,"("))&Text.End(,Text.Length()-Text.PositionOf(_,")")-1), type text}})
There is an even simpler solution.
You can create a new function called fun_ReplaceTextBetweenDelimiters, and in it add this code 👇
let
fun_ReplaceTextBetweenDelimiters = (Text as text, StartDelimiter as text, EndDelimiter as text, optional ReplaceDelimiters as nullable logical, optional NewText as nullable text, optional TrimResult as nullable logical, optional FixDoubleSpaces as nullable logical) as text =>
let
// Add Default Parameters
Default_ReplaceDelimiters = if ReplaceDelimiters is null then true else ReplaceDelimiters,
Default_NewText = if NewText is null then "" else NewText,
Default_TrimResult = if TrimResult is null then true else TrimResult,
Default_FixDoubleSpaces = if FixDoubleSpaces is null then true else FixDoubleSpaces,
//Do work
TextBetweenDelimiters = Text.BetweenDelimiters(Text, StartDelimiter, EndDelimiter),
TextToReplace = if Default_ReplaceDelimiters then Text.Combine({StartDelimiter,TextBetweenDelimiters,EndDelimiter}) else TextBetweenDelimiters,
ReplacedText = Text.Replace(Text, TextToReplace, Default_NewText),
//Clean Result
TrimmedText = if Default_TrimResult then Text.Trim(ReplacedText) else ReplacedText,
FixedSpaces = if Default_FixDoubleSpaces then Text.Replace(TrimmedText, " ", " ") else TrimmedText
in
FixedSpaces
in
fun_ReplaceTextBetweenDelimiters
Then, we can test it like this:
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WcnRyVtAw1FTSAbGUYnWilVzd3BU0jEAiQBZYJCIyCqhGRwEsCOQoxcYCAA==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [TestData = _t, TargetData = _t]),
ChangeType = Table.TransformColumnTypes(Source,{{"TestData", type text}, {"TargetData", type text}}),
RunFunction = Table.AddColumn(ChangeType, "NewText", each fun_ReplaceTextBetweenDelimiters([TestData], "(", ")", true), type text),
TestResult = Table.AddColumn(RunFunction, "Test", each [TargetData]=[NewText], type logical)
in
TestResult
Input:
TestData
TargetData
ABC (1)
ABC
EFG (2)
EFG
XYZ (1, 2)
XYZ
Output:
TestData
TargetData
NewText
Test
ABC (1)
ABC
ABC
TRUE
EFG (2)
EFG
EFG
TRUE
XYZ (1, 2)
XYZ
XYZ
TRUE
I have a bunch of excel files that are formatted in a pretty weird way that I would like to make an automated import script for, so I can easily get this data into a sheet with the correct formatting.
It has hourly values for each month in the first 12 columns, then the date along with hours in the next 12 columns.
What I would like to do is be able to get this data in to a table where the first column has the date and hour (in excel format) and the second one containing the data. My thought was to record a macro during the process of adjusting the data with power query and then repeating the macro with multiple files. However, I can't seem to find a good way to make "Column 2"'s data moved to the end of "Column 1" and repeating that for both values and the dates using Power Query. Any pointers?
Also notice, column 1s length differs from column 2 since January has more values than February. However, column 1s lenght is the same as column 13, column 2s length same as 14 and so on.
I have uploaded a sample file here
Create a blank query from scratch (on my machine I do this in Excel via: Data > Get Data > From Other Sources > Blank Query).
Click Home > Advanced Editor, copy-paste the code below and change this line folderPath = "C:\Users\user\", to the path of the parent folder that contains the Excel files. Then click Close & Load.
As the data is being imported from multiple workbooks (and possibly multiple sheets), the first two columns of the loaded table should be the workbook and worksheet that that row of data came from. (If you want to get rid of the first two columns, edit the query.)
let
folderPath = "C:\Users\user\",
getDataFromSheet = (sheetData as table) =>
let
promoteHeaders = Table.PromoteHeaders(sheetData, [PromoteAllScalars=true]),
standardiseHeaders =
let
headers = Table.ColumnNames(promoteHeaders),
zipWithLowercase = List.Zip({headers, List.Transform(headers, Text.Lower)}),
renameAsLowercase = Table.RenameColumns(promoteHeaders, zipWithLowercase)
in
renameAsLowercase,
emptyTable = Table.FromColumns({{},{}}, {"Date", "Value"}),
monthsToLoopOver = {"januari", "februari", "mars", "april", "maj", "juni", "juli", "augusti", "september", "oktober", "november", "december"},
appendEachMonth = List.Accumulate(monthsToLoopOver, emptyTable, (tableState, currentMonth) =>
let
selectColumns = Table.SelectColumns(standardiseHeaders, {currentMonth & "tim", currentMonth}, MissingField.UseNull),
renameColumns = Table.RenameColumns(selectColumns, {{currentMonth & "tim", "Date"}, {currentMonth, "Value"}}),
appendToTable = Table.Combine({tableState, renameColumns})
in
appendToTable
),
tableOrNull = if List.Contains(Table.ColumnNames(standardiseHeaders), "januari") then appendEachMonth else null
in
tableOrNull,
getDataFromWorkbook = (filePath as text) =>
let
workbookContents = Excel.Workbook(File.Contents(filePath)),
sheetsOnly = Table.SelectRows(workbookContents, each [Kind] = "Sheet"),
invokeFunction = Table.AddColumn(sheetsOnly, "f", each getDataFromSheet([Data]), type table),
appendAndExpand =
let
selectColumnsAndRows = Table.SelectColumns(Table.SelectRows(invokeFunction, each not ([f] is null)), {"Name", "f"}),
renameColumns = Table.RenameColumns(selectColumnsAndRows, {{"Name", "Sheet"}}),
expandColumn = Table.ExpandTableColumn(renameColumns, "f", {"Date", "Value"})
in
expandColumn
in
appendAndExpand,
filesInFolder = Folder.Files(folderPath),
validFilesOnly = Table.SelectRows(filesInFolder, each [Extension] = ".xlsx"),
invokeFunction = Table.AddColumn(validFilesOnly, "f", each getDataFromWorkbook([Folder Path] & [Name])),
appendAndExpand =
let
selectRowsAndColumns = Table.SelectColumns(Table.SelectRows(invokeFunction, each not ([f] is null)), {"Name", "f"}),
renameColumns = Table.RenameColumns(selectRowsAndColumns, {{"Name", "Workbook"}}),
expandColumn = Table.ExpandTableColumn(renameColumns, "f", {"Sheet", "Date", "Value"})
in
expandColumn,
excludeBlankDates = Table.SelectRows(appendAndExpand, each not (Text.StartsWith([Date], " "))),
transformTypes =
let
dateAndHour = Table.TransformColumns(excludeBlankDates, {{"Date", each Text.Split(_, " ")}}),
changeTypes = Table.TransformColumns(dateAndHour, {{"Workbook", Text.From, type text}, {"Sheet", Text.From, type text}, {"Date", each DateTime.From(_{0}) + #duration(0, Number.From(_{1}), 0, 0), type datetime}, {"Value", Number.From, type number}})
in
changeTypes
in
transformTypes
For reliability and robustness, it would be good if you create a folder and put all Excel files (that need restructuring) into that folder -- and ensure nothing else goes into that folder (not even the file that will be doing the importing/restructuring).
If you can't do this for whatever reason, then click the validFilesOnly step whilst in the Query Editor and amend the filter criteria, such that the table only includes files you want restructured.
You can do the formatting pretty quickly by writing your own macro in VBA. Then you can create another macro to run the formatting macro on multiple files within a folder.
Run same excel macro on multiple excel files
Here is an example of a macro that will reformat your data closer to what you are looking for.
Sub FormatBlad()
' create a new sheet and rename it
Sheets.Add After:=ActiveSheet
Sheets(Sheets.Count).Name = "Formatted"
' set helper variables
Dim blad As Worksheet
Dim format As Worksheet
Set blad = Sheets("Blad1")
Set ft = Sheets("Formatted")
Dim blad_row_num As Integer
Dim ft_row_num As Integer
Dim month_offset As Integer
blad_row_num = 2
ft_row_num = 2
month_offset = 13 ' column N - 1
' set column headers in formatted sheet
ft.Range("A1").Value = "Date"
ft.Range("B1").Value = "Value"
' loop through months
For i = 1 To 12
blad_row_num = 2
While blad.Cells(blad_row_num, i).Value <> ""
ft.Cells(ft_row_num, 1).Value = blad.Cells(blad_row_num, month_offset + i).Value
ft.Cells(ft_row_num, 2).Value = blad.Cells(blad_row_num, i).Value
blad_row_num = blad_row_num + 1
ft_row_num = ft_row_num + 1
Wend
Next i
End Sub