How to handle null values in custom function? - powerbi

I'm trying to add a custom column to combine values of 2 columns (Col3 and Col4) with the result of a custom function fnMyFunction() in this way
#"Added Custom" = Table.AddColumn(#"Previous Step", "Custom Column",
each
Text.Combine(
{
[Col3],
[Col4],
fnMyFunction([Col5],[Col6])
}
)),
I'm getting this error when function handles null values
Expression.Error: We cannot convert the value null to type Text.
Details:
Value=
Type=[Type]
The function fnMyFunction is like this:
(input1 as text, input2 as text)=>
let
Inputs = {input1, input2},
SplitAndZip = List.Zip(List.Transform(Inputs, each Text.ToList(_))),
OtherStep
...
..
LastStep
in
LastStep
I've tried to add the if else in Input step in order to get empty as output for the function but doesn't work
(input1 as text, input2 as text)=>
let
Inputs = if input1 <> null then {input1, input2} else {"",""}, //Added "if else" here
SplitAndZip = List.Zip(List.Transform(Inputs, each Text.ToList(_))),
OtherSteps
...
..
LastStep
in
LastStep
How can be fix this?

Change your function definition to be the following:
(optional input1 as text, optional input2 as text)=>

PQ has a null-coalesce operator ??. Try this:
Inputs = {input1 ?? "", input2 ?? ""}
edit from the future - #2 is wrong. My bad. Still, read Ben's guide. It's the best PQ text-book there is.
Once you fix the fn, each... Combine([col3],[col4]..) will break because you forgot to _ before [col3] and [col4]. each..._ is syntactic sugar for a single-argument function (eating a whole row, in this case). See here: https://bengribaudo.com/blog/2017/12/08/4270/power-query-m-primer-part3-functions-function-values-passing-returning-defining-inline-recursion

Use
each try Text.Combine() otherwise ""

Related

Casting regex match to String

I created a simple code in Scala that checks whether an input is correctly formatted as HH:mm. I expect the code to result in an Array of valid strings. However, what I'm getting as a result is of type Any = Array(), which is problematic as when I try to print that result I get something like that:
[Ljava.lang.Object;#32a59591.
I guess it's a simple problem but being a Scala newbie I didn't manage to solve it even after a good few hours of googling and trial & error.
val scheduleHours = if (inputScheduleHours == "") {
dbutils.notebook.exit(s"ERROR: Missing param value for schedule hours.")
}
else {
val timePattern = """^((?:[0-30]?[0-9]|2[0-3]):[0-5][0-9])$""".r
val inputScheduleHoursParsed = inputScheduleHours.split(";").map(_.trim)
for (e <- inputScheduleHoursParsed) yield e match {
case timePattern(e) => e.toString
case _ => dbutils.notebook.exit(s"ERROR: Wrong param value for schedule hours: '${inputScheduleHours}'")
}
}
The problem is that some branches return the result you want and others return dbutils.notebook.exit which (I think) returns Unit. Scala must pick a type for the result that is compatible with both Unit and Array[String], and Any is the only one that fits.
One solution is to add a compatible value after the calls to dbutils.notebook.exit, e.g.
val scheduleHours = if (inputScheduleHours == "") {
dbutils.notebook.exit(s"ERROR: Missing param value for schedule hours.")
Array.empty[String]
}
Then all the branches return Array[String] so that will be the type of the result.

Regexp Match Google Script inside a loop?

I've been on this problem for a couple of hours, I'm new to coding, so excuse me if it's a very simple question.
So I have a list of text and I want to find if there is one of the regular expression from the other sheet in every cell.
If yes, paste the regular expression next to the text.
Example:
For the first row:
7063 BIO PLANET LIEGE.
--> i'd like it to write "BIO PLANET" in the cell to the right. (Because BIO PLANET is one of the regular expression to test from the second sheet).
I wrote something like this, but couldn't really figure out what needs to be fixed:
function ExpenseMatching() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet1 = spreadsheet.getSheetByName("Import2");
var sheet2 = spreadsheet.getSheetByName("Regular Expression");
for ( i =1; i<24 ; i++)
{
//Browser.msgBox(i)
var test1 = sheet2.getRange("A"+ i);
var test2 = sheet1.getRange("A2");
var test = new RegExp(test1).test(test2);
if (regexp==true)
{
test1.copyTo(sheet1.getRange("I2"));
Browser.msgBox(test)
}
else
{
}
}
}
Thanks is advance for your help guys !
You want to retrieve the values of the column "A" on the sheet Import2 and the values of the column "A" on the sheet Regular Expression.
You want to check whether the values of Import2 includes the values of Regular Expression. When the values of Import2 includes the values of Regular Expression, you want to put the value of Regular Expression to the column "B" on Import2.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer?
Modification points:
In your script,
if (regexp==true) doesn't work and an error occurs. Because regexp is not declared.
This has already been mentioned by Rubén's comment.
From your question, I thought that you want to put the result value to the column "B" of Import2. But it seems that your script puts the value to the column "I" from test1.copyTo(sheet1.getRange("I2")).
Your script checks only "A2" of Import2.
Each row is checked and copy the value in the for loop. In this case, the process cost will be high.
When above points are reflected to your script, how about the following modified script?
Modified script:
function ExpenseMatching() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet1 = spreadsheet.getSheetByName("Import2");
var sheet2 = spreadsheet.getSheetByName("Regular Expression");
const values1 = sheet1.getRange(`A2:A${sheet1.getLastRow()}`).getValues();
const values2 = sheet2.getRange(`A2:A${sheet2.getLastRow()}`).getValues();
const res = values1.map(([r1]) => {
for (let i = 0; i < values2.length; i++) {
if (new RegExp(values2[i][0]).test(r1)) {
return [values2[i][0]];
}
}
return [""];
});
sheet1.getRange(2, 2, res.length, 1).setValues(res);
}
I think that in your situation, you can also use if (r1.includes(values2[i][0])) { instead of if (new RegExp(values2[i][0]).test(r1)) {. This might be able to reduce more cost.
Note:
In this modification, the result values are put to the column "B" of Import2.
Please run the script with enabling V8.
References:
map()
setValues()

regarding Regex operation in KNIME

This below formula is in alteryx workflow.
if(REGEX_Match([CurrentField],' (',0)) then 'string to display something'
elseif(REGEX_Match([CurrentField],'a',0)) then 'another string to display'
Can you explain me what above function is trying to perform and how to achieve this in KNIME.
To achieve this if/else syntax in KNIME you can use Column Expressions node. In this node there is also rexegMatcher function that returns Boolean.
It can be something like this:
if (regexMatcher(column("column1"),"your_regex" )) {
"string1"
} else if (regexMatcher(column("column1"),"your_regex2" )) {
"string2"
} else {
"string3"
}

Remove everything but numbers from a cell

I have an excel sheet where i use the follwoing command to get numbers from a cell that contains a form text:
=MID(D2;SEARCH("number";D2)+6;13)
It searches for the string "number" and gets the next 13 characters that comes after it. But some times the results get more than the number due to the fact these texts within the cells do not have a pattern, like the example below:
62999999990
21999999990
11999999990
6299999993) (
17999999999)
21914714753)
58741236714 P
18888888820
How do i avoid taking anything but numbers OR how do i remove everything but numbers from what i get?
You can user this User Defined Function (UDF) that will get only the numbers inside a specific cell.
Code:
Function only_numbers(strSearch As String) As String
Dim i As Integer, tempVal As String
For i = 1 To Len(strSearch)
If IsNumeric(Mid(strSearch, i, 1)) Then
tempVal = tempVal + Mid(strSearch, i, 1)
End If
Next
only_numbers = tempVal
End Function
To use it, you must:
Press ALT + F11
Insert new Module
Paste code inside Module window
Now you can use the formula =only_numbers(A1) at your spreadsheet, by changing A1 to your data location.
Example Images:
Inserting code at module window:
Executing the function
Ps.: if you want to delimit the number of digits to 13, you can change the last line of code from:
only_numbers = tempVal
to
only_numbers = Left(tempVal, 13)
Alternatively you can take a look a this topic to understand how to achieve this using formulas.
If you are going to go to a User Defined Function (aka UDF) then perform all of the actions; don't rely on the preliminary worksheet formula to pass a stripped number and possible suffix text to the UDF.
In a standard code module as,
Function udfJustNumber(str As String, _
Optional delim As String = "number", _
Optional startat As Long = 1, _
Optional digits As Long = 13, _
Optional bCaseSensitive As Boolean = False, _
Optional bNumericReturn As Boolean = True)
Dim c As Long
udfJustNumber = vbNullString
str = Trim(Mid(str, InStr(startat, str, delim, IIf(bCaseSensitive, vbBinaryCompare, vbTextCompare)) + Len(delim), digits))
For c = 1 To Len(str)
Select Case Asc(Mid(str, c, 1))
Case 32
'do nothing- skip over
Case 48 To 57
If bNumericReturn Then
udfJustNumber = Val(udfJustNumber & Mid(str, c, 1))
Else
udfJustNumber = udfJustNumber & Mid(str, c, 1)
End If
Case Else
Exit For
End Select
Next c
End Function
I've used your narrative to add several optional parameters. You can change these if your circumstances change. Most notable is whether to return a true number or text-that-looks-like-a-number with the bNumericReturn option. Note that the returned values are right-aligned as true numbers should be in the following supplied image.
By supplying FALSE to the sixth parameter, the returned content is text-that-looks-like-a-number and is now left-aligned in the worksheet cell.
If you don't want VBA and would like to use Excel Formulas only, try this one:
=SUMPRODUCT(MID(0&MID(D2,SEARCH("number",D2)+6,13),LARGE(INDEX(ISNUMBER(--MID(MID(D2,SEARCH("number",D2)+6,13),ROW($1:$13),1))* ROW($1:$13),0),ROW($1:$13))+1,1)*10^ROW($1:$13)/10)

How to do a single line If statement in VBScript for Classic-ASP?

The "single line if statement" exists in C# and VB.NET as in many other programming and script languages in the following format
lunchLocation = (dayOfTheWeek == "Tuesday") ? "Fuddruckers" : "Food Court";
does anyone know if there is even in VBScript and what's the extact syntax?
The conditional ternary operator doesn't exist out of the box, but it's pretty easy to create your own version in VBScript:
Function IIf(bClause, sTrue, sFalse)
If CBool(bClause) Then
IIf = sTrue
Else
IIf = sFalse
End If
End Function
You can then use this, as per your example:
lunchLocation = IIf(dayOfTheWeek = "Tuesday", "Fuddruckers", "Food Court")
The advantage of this over using a single line If/Then/Else is that it can be directly concatenated with other strings. Using If/Then/Else on a single line must be the only statement on that line.
There is no error checking on this, and the function expects a well formed expression that can be evaluated to a boolean passed in as the clause. For a more complicated and comprehensive answer see below. Hopefully this simple response neatly demonstrates the logic behind the answer though.
It's also worth noting that unlike a real ternary operator, both the sTrue and sFalse parameters will be evaluated regardless of the value of bClause. This is fine if you use it with strings as in the question, but be very careful if you pass in functions as the second and third parameters!
VBScript does not have any ternary operator.
A close solution in a single line and without using a user defined function, pure VBScript:
If dayOfTheWeek = "Tuesday" Then lunchLocation = "Fuddruckers" Else lunchLocation = "Food Court"
BTW, you can use JScript in Classic ASP if ternary opertor is so important to you.
edited 2017/01/28 to adapt to some of the non boolean condition arguments
Note: If all you need is to select an string based on an boolean value, please, use the code in the Polinominal's answer. It is simpler and faster than the code in this answer.
For a simple but more "flexible" solution, this code (the original code in this answer) should handle the usual basic scenarios
Function IIf( Expression, TruePart, FalsePart)
Dim bExpression
bExpression = False
On Error Resume Next
bExpression = CBool( Expression )
On Error Goto 0
If bExpression Then
If IsObject(TruePart) Then
Set IIf = TruePart
Else
IIf = TruePart
End If
Else
If IsObject(FalsePart) Then
Set IIf = FalsePart
Else
IIf = FalsePart
End If
End If
End Function
If uses the Cbool function to try to convert the passed Expression argument to a boolean, and accepts any type of value in the TrueValue and FalseValue arguments. For general usage this is fast, safe and fully complies to documented VBScript behaviour.
The only "problem" with this code is that the behaviour of the CBool is not fully "intuitive" for some data types, at least for those of us that constantly change between vbscript and javascript. While numeric values are coherent (a 0 is a False and any other numeric value is a True), non numeric types generate a runtime error (in previous code handled as false), except if it is a string with numeric content or that can be interpreted as true or false value in english or in the OS locale.
If you need it, a VBScript version "equivalent" to the ? javascript ternary operator is
Function IIf( Expression, TruePart, FalsePart )
Dim vType, bExpression
vType = VarType( Expression )
Select Case vType
Case vbBoolean : bExpression = Expression
Case vbString : bExpression = Len( Expression ) > 0
Case vbEmpty, vbNull, vbError : bExpression = False
Case vbObject : bExpression = Not (Expression Is Nothing)
Case vbDate, vbDataObject : bExpression = True
Case Else
If vType > 8192 Then
bExpression = True
Else
bExpression = False
On Error Resume Next
bExpression = CBool( Expression )
On Error Goto 0
End If
End Select
If bExpression Then
If IsObject( TruePart ) Then
Set IIf = TruePart
Else
IIf = TruePart
End If
Else
If IsObject( FalsePart ) Then
Set IIf = FalsePart
Else
IIf = FalsePart
End If
End If
End Function
BUT independently of the version used, be careful, you are calling a function, not using a ternary operator. Any code, or function call you put in TruePart of FalsePart WILL BE EXECUTED independently of the value of the condition. So this code
value = IIf( 2 > 3 , DoSomething(), DontDoSomething() )
WILL EXECUTE the two functions. Only the correct value will be returned to value var.
There's a weird trick possible (hi, Python!) for exact one-liner:
lunchLocation = array("Food Court", "Fuddruckers")(-(dayOfTheWeek = "Tuesday"))
The "magic" works because of a boolean operation specifics in VBScript.
True is actually -1 and False is 0, therefore we can use it as an index for array (just get rid of a minus). Then the first item of array will be a value for False condition and second item for True.
related to #MC_ND answer:
to execute only one function, you can do something like that:
If VarType(TruePart) = vbString and InStr(1,TruePart,"function:") = 1 then
IIf = GetRef(Mid(TruePart,10))()
Else
IIf = TruePart
End If
the same for the FalsePart, and call IIf() it like that:
value = IIf( 2 > 3 , "function:DoSomething", "function:DontDoSomething" )
and will call DoSomething() or DontDoSomething()