Power query recursive function: add more steps after the recursive step - excel

Background:
I have posted a question regarding a custom function in Power Query that I found in a blog by Chris Webb which I have already got an answer to.But now I have another question related to the same custom function.
One of the amazing steps in that custom function is the recursive step at the end named "OutputTable" which calls itself using a if statement, basically making it a loop. Below is the step:
OutputTable = if NextColumnNumber>(Table.ColumnCount(ExpandedTable)-1) then ExpandedTable else ExpandAll(ExpandedTable, NextColumnNumber)
Question:
Now what I would like to do after this step is to be able to add more transformation on the OutputTable.
For Example, I would like to add a column with just "A" in all the rows. The syntax to do that would be AddNewColumn = Table.AddColumn(OutputTable, "Test", each "A"). But when I do this this gives me an error saying that the column "Test" already exists. But i'm sure that there is no other column with name "Test". Even if I try changing the name of the column to anything else, I get the same error.
Note: Although the actual step I want to add is not AddColumn, I think I can figure out that part If I get a solution for this.
SourceCode:
let
Source = (TableToExpand as table, optional ColumnNumber as number) =>
let
ActualColumnNumber = if (ColumnNumber=null) then 0 else ColumnNumber,
ColumnName = Table.ColumnNames(TableToExpand){ActualColumnNumber},
ColumnContents = Table.Column(TableToExpand, ColumnName),
ColumnsToExpand = List.Select(List.Distinct(List.Combine(List.Transform(ColumnContents, each if _ is table then Table.ColumnNames(_) else {}))), each (_ = "view" or _ = "viewfolder" or _ = "Attribute:name")),
NewColumnNames = List.Transform(ColumnsToExpand, each ColumnName & "." & _),
CanExpandCurrentColumn = List.Count(ColumnsToExpand)>0,
ExpandedTable = if CanExpandCurrentColumn then Table.ExpandTableColumn(TableToExpand, ColumnName, ColumnsToExpand, NewColumnNames) else TableToExpand,
NextColumnNumber = if CanExpandCurrentColumn then ActualColumnNumber else ActualColumnNumber+1,
OutputTable = if NextColumnNumber>(Table.ColumnCount(ExpandedTable)-1) then ExpandedTable else ExpandAll(ExpandedTable, NextColumnNumber)
in
OutputTable
in
Source

I'm guessing it's throwing the error due to the recursive nature of the function calling itself and trying to apply the new column twice, once in the innermost loop and once in the outermost loop.
Let's say we have a table with two columns Col1 and Col2 that need to be expanded. If you add the new column after the OutputTable step, you'll get:
Start: Col0, Col1, Col2
OutputTable(1): Col0, Col1.a, Col1.b, Col2
OutputTable(2): Col0, Col1.a, Col1.b, Col2.x, Col2.y, Col2.z, Test
AddNewColumn: Col0, Col1.a, Col1.b, Col2.x, Col2.y, Col2.z, Test, Test
Here are a couple of approaches to try:
1. Only try to add the column when recursion is finished.
I think you can do this by changing your OutputTable line as follows:
OutputTable = if NextColumnNumber>(Table.ColumnCount(ExpandedTable)-1)
then Table.AddColumn(ExpandedTable, "Test", each "A")
else ExpandAll(ExpandedTable, NextColumnNumber)
2. Check if the column exists before trying to add it.
AddNewColumn = if Table.HasColumns(OutputTable, "Test")
then OutputTable
else Table.AddColumn(OutputTable, "Test", each "A")

Related

Replace value in one column with another

I have some doubts, i want to replace the values in column for the values of the other one, i have my query like this
= Table.ReplaceValue(#"Renamed Columns1", each ["Alcance-Canal"], null , Replacer.ReplaceValue,{"CANAL/ES"})
however it still doesn't work, could you please assist me?
Regards
Easiest thing to do is add a custom column with formula
= if [columnname] = "something" then [columnname] else [othercolumnname]
or
= if [columnname] = "something" then "anotherthing" else [othercolumnname]

Importing CustomDocumentProperties from Word to Excel using VBA

I am trying to pull CustomDocumentProperties from a Word Document (that I select using Application.GetOpenFilename) to an Excel Sheet.
I can get the to run code using the number of the item:
Set ExcelRange = Range("DataFields")
For r = 1 To ExcelRange.Rows.Count Step 1
ExcelWorkbook.Sheets("Sheet1").Cells(r, 3) = WordDoc.CustomDocumentProperties(r).Value
Next r
If I hard code the name of the custom property, it also works:
Set ExcelRange = Range("DataFields")
For r = 1 To ExcelRange.Rows.Count Step 1
ExcelWorkbook.Sheets("Sheet1").Cells(r, 3) = WordDoc.CustomDocumentProperties("Subject Name").Value
Next r
Obviously in this case, it returns the "Subject Name" 7 times).
I don't want to return all the values - only specific ones based on the values of a named range (in this case DataFields).
DataFields references A1:A7. I would like to take those cells(that contain the names of the custom properties) and paste the value of the corresponding custom properties in C1:C7
However, I can seem to get the code to return values in C1:C7 based on the values in A1:A7.
Based on my (limited) knowledge, I thought that the following would return the desired results, but it's not working for me:
Set ExcelRange = Range("DataFields")
For r = 1 To ExcelRange.Rows.Count Step 1
ExcelWorkbook.Sheets("Sheet1").Cells(r, 3) = WordDoc.CustomDocumentProperties(ExcelRange(r, 1))
Next r
Any help would be appreciated.
Thanks!

Keep getting ~N/A

I have a simple 2 column array and all I want to do is use Vlookup to find the matching value in Column 1 and return the value out of column 2. I keep getting #N/A which implies that my "answer" isn't there. I thought I had done everything correctly, but I can't seem to find the answer despite reading MANY #N/A questions. This is my code:-
Dim y as Variant
Dim Misc_Pay(1 to 16,1 to 2 ) as Variant.
' Populate the Misc_Pay array
Misc_Pay(1,2) = "Cancer,3"
Misc_Pay(2,2) = "Clerical,Last of Month"
Misc_Pay(3,2) = "Halifax,14"
Misc_Pay(4,2) = "Reward,1"
'and so on down to
Misc_Pay(16,2) = "Last Line,End of File"
'My Vlook up code is:-
y = (Application.Vlookup("Reward",Misc_Pay,2,False)
I've then been printing "y" out so I can see what value it is picking up, except it doesn't appear to be picking anything up, but e.g. "Reward" is patently there.
If I can get this line to work then the full line code will be:-
If(iserror(Application.Vlookup("Reward,Misc_Bay,2,False) then 'do something' Else 'do something else'
To start, the code you posted won't compile, due to the period at the end on line 2, and the extra ( on the last line.
If you're trying to do a lookup in a 2D array, you need to set values for both the key and lookup columns (in this case, columns 1 and 2 respectively). Currently, you're just assigning the entire string with the comma to the second column, so there's nothing to look up in the first column.
I think what you want is this:
Dim y As Variant
Dim Misc_Pay(1 To 16, 1 To 2) As Variant
Misc_Pay(1, 1) = "Cancer"
Misc_Pay(1, 2) = "3"
Misc_Pay(2, 1) = "Clerical"
Misc_Pay(2, 2) = "Last of Month"
Misc_Pay(3, 1) = "Halifax"
Misc_Pay(3, 2) = "14"
Misc_Pay(4, 1) = "Reward"
Misc_Pay(4, 2) = "1"
Misc_Pay(16, 1) = "Last Line"
Misc_Pay(16, 2) = "End of File"
y = Application.VLookup("Reward", Misc_Pay, 2, False)

Matlab Data Preprocessing and Dynamic Struct Assignments

I'm quite new to Matlab and I'm struggling trying to figure out how to properly preprocess my data in order to make some calculations with it.
I have an Excel table with financial log returns of many companies such that every row is a day and every column is a company:
I imported everything correctly into Matlab like this:
Now I have to create what's caled "rolling windows". To do this I use the following code:
function [ROLLING_WINDOWS] = setup_returns(RETURNS)
bandwidth = 262;
[rows, columns] = size(RETURNS);
limit_rows = rows - bandwidth;
for i = 1:limit_rows
ROLLING_WINDOWS(i).SYS = RETURNS(i:bandwidth+i-1,1);
end
end
Well if I run this code for the first column of returns everything works fine... but my aim is to produce the same thing for every column of log returns. So basically I have to add a second for loop... but what I don't get is which syntax I need to use in order to make that ".SYS" dynamic and based on my array of string cells containing company names so that...
ROLLING_WINDOWS(i)."S&P 500" = RETURNS(i:bandwidth+i-1,1);
ROLLING_WINDOWS(i)."AIG" = RETURNS(i:bandwidth+i-1,2);
and so on...
Thanks for your help guys!
EDIT: working function
function [ROLLING_WINDOWS] = setup_returns(COMPANIES, RETURNS)
bandwidth = 262;
[rows, columns] = size(RETURNS);
limit_rows = rows - bandwidth;
for i = 1:limit_rows
offset = bandwidth + i - 1;
for j = 1:columns
ROLLING_WINDOWS(i).(COMPANIES{j}) = RETURNS(i:offset, j);
end
end
end
Ok everything is perfect... just one question... matlab intellissense tells me "ROLLING_WINDOWS appears to change size on every loop iteration bla bla bla consider preallocating"... how can I perform this?
You're almost there. Use dynamic field names by building strings for fields. Your fields are in a cell array called COMPANIES and so:
function [ROLLING_WINDOWS] = setup_returns(COMPANIES, RETURNS)
bandwidth = 262;
[rows, columns] = size(RETURNS);
limit_rows = rows - bandwidth;
%// Preallocate to remove warnings
ROLLING_WINDOWS = repmat(struct(), limit_rows, 1);
for i = 1:limit_rows
offset = bandwidth + i - 1;
for j = 1:columns
%// Dynamic field name referencing
ROLLING_WINDOWS(i).(COMPANIES{j}) = RETURNS(i:offset, j);
end
end
end
Here's a great article by Loren Shure from MathWorks if you want to learn more: http://blogs.mathworks.com/loren/2005/12/13/use-dynamic-field-references/ ... but basically, if you have a string and you want to use this string to create a field, you would do:
str = '...';
s.(str) = ...;
s is your structure and str is the string you want to name your field.

VBA - Excel : Vlookup crashes my program when no match found

In my program, the user types a Zip Code and gets as an output information related to the Zip Code (province, city, district). To do this, I use the Vlookup function.
So, the user :
Types a Zip code in the main sheet
The program search in a database (in another sheet) in which Zip Code are associated to City, Province, District.
When there is a match, it sends the result to the main pages, so the user can get a city, province, district just by typing the Zip Code. Quite simple process.
I use this code to do so :
If Range("J9").Value <> "N/A" Then 'if there is actually a zip code entered by the user (if not, it will be "N/A")
cityZip = Application.WorksheetFunction.VLookup(sMain.Range("J9").Value,
sZipCodes.Range("B2:E864"), 3, False)
barangayZip = Application.WorksheetFunction.VLookup(sMain.Range("J9").Value,
sZipCodes.Range("B2:E864"), 2, False)
provinceZip = Application.WorksheetFunction.VLookup(sMain.Range("J9").Value,
sZipCodes.Range("B2:E864"), 4, False)
sMain.Range("J7").Value = provinceZip
sMain.Range("J13").Value = cityZip
sMain.Range("J16").Value = barangayZip
Else
End If
It works perfectly when there is a Zip Code which is in my database. But if not, it crashes the execution of the program and I have an error message (like "execution error '1004', unable to read the Vlookup ...).
How to modify my code to just say that if there is no match, then it should just do nothing? I don't know how to introduce this request in a Vlookup function.
Thanks in advance !
EDIT : here is my new code, after following Tim Williams suggestion :
'Using Zip Code
If Range("J9").Value <> "N/A" Then
provinceZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907"), 4, False)
If IsError(provinceZip) = False Then
cityZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907"), 3, False)
barangayZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907"), 2, False)
sMain.Range("J7").Value = provinceZip
sMain.Range("J13").Value = cityZip
sMain.Range("J16").Value = barangayZip
Else
'do nothing
End If
End If
My error is on this line :
provinceZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907"), 4, False)
=> Error 1004, invalid number of arguments
You should read up on VBA error handling. A source such as http://www.cpearson.com/excel/errorhandling.htm might help. That said, try the following code.
You want something like:
Public Function SafeVlookup(lookup_value, table_array, _
col_index, range_lookup, error_value) As Variant
On Error Resume Next
Err.Clear
return_value = Application.WorksheetFunction.VLookup(lookup_value, _
table_array, col_index, range_lookup)
If Err <> 0 Then
return_value = error_value
End If
SafeVlookup = return_value
On Error GoTo 0
End Function
In your code you might call it like:
cityZip = SafeVlookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E864"), 3, _
False, "")
The last parameter is the default value to return if the vlookup failed. So in this example it'd return an empty string.
I usually wrap the vlookup() with an iferror() which contains the default value.
The syntax would be as follows:
iferror(vlookup(....), <default value when lookup fails>)
You can also do something like this:
Dim result as variant
result = Application.vlookup(......)
If IsError(result) Then
' What to do if an error occurs
Else
' what you would normally do
End if
You changed from Vlookup to Lookup, which has less arguments. Using only 2 arguments, you should be fine: provinceZip = Application.Lookup(sMain.Range("J9").Value, sZipCodes.Range("B2:E907") )
SafeVlookup is a good function.
I am still learning VB.
I changed like this and it works for me.
Function SafeVlookup(lookup_value, _
range_lookup, col_index, error_value) As Variant
.....
return_value = Application.WorksheetFunction.vlookup(lookup_value, _
range_lookup, col_index, error_value)
....
End Function
Hope I can use it like this.

Resources