Powerquery IN operator in conditional column - powerbi

How this m Power BI M code can be modified
= Table.AddColumn(#"PreviousStep", "Tag", each
if Text.Contains([Column1]) = "value1" then "bingo"
else if Text.Contains([Column1]) = "value2" then "bingo"
else if Text.Contains([Column1]) = "value3" then "bingo"
else ["Some other value"])
into a one line code similar to SQL
case when [Column1] in ("value1", "value2", "value3") then "bingo" else "Some other value" end
I would not like to repeat the lines else if but have it in a similar way as
List.Contains({'Value1', 'Value2', 'Value3'}, [Column1])
used here: https://stackoverflow.com/a/51749519/1903793

If you want to compare whole word, you should use List.ContainsAny function
let
haystack = #table({"col"}, {{"qwer"}, {"asdf"}, {"zxcv"}, {"zxwecv"}, {"other"}}),
needles = {"qwer", "zxcv"},
add = Table.AddColumn(haystack, "Tag", each if List.ContainsAny(needles, {[col]}) then "bingo" else "Some other value")
in
add
But if you search a part of word, the answer becomes a little more complicated
let
haystack = #table({"col"}, {{"qwer"}, {"asdf"}, {"zxcv"}, {"zxwecv"}, {"other"}}),
needles = {"we", "as"},
add = Table.AddColumn(haystack, "Tag", each if List.MatchesAny(needles, (s)=>Text.Contains([col], s)) then "bingo" else "Some other value")
in
add

Or if you want to return the string that matches, you can use the List.Accumulate function:
List.Accumulate(YOUR_LIST_OF_STRINGS, null, (state, current) => if Text.Contains([YOUR COLUMN NAME], current) then current else state)
The only disadvantage to this method is that if there are multiple matches it will return only the last...
Here's a more complex version that returns a list of the matched strings:
List.Accumulate(YOUR_LIST_OF_STRINGS, {}, (state, current) => if Text.Contains([YOUR COLUMN NAME], current) then List.Combine({{current}, state}) else state)
Or you could amend that so that it returns a comma delimited list as a string etc., or whatever.

You have to use List.Transform to generate the Text.Contains function call, then use List.AnyTrue to check if Column1 contains any of the text.
= Table.AddColumn(#"PreviousStep", "Tag", each if List.AnyTrue(List.Transform({"value1", "value2", "value3"}, (s) => Text.Contains([Column1], (s)))) then "bingo" else "Some other value")
Results:
Reference

Related

Compare of values from two lists by use of regular expressions in Kotlin

I have two lists. The first contains original product data as following:
data class InputProductData (val optionFamilyInput: String?, val optionCodeInput: String?, val optionDescriptionInput: String?)
val inputProductData = mutableListOf(
InputProductData("AAA", "111","Chimney with red bricks"),
InputProductData(null,"222","Two wide windows in the main floor"),
InputProductData("CCCC",null,"Beautiful door in green color"),
InputProductData("DDDD",null,"House with area 120 square meters"),
InputProductData(null,"555","Old wood windows")
)
Second list consists of customizing data. The list can have many identical option ids (first column).
data class CustomizingProductOption(val id: Int, val optionName: String, val optionCategory: String, val optionFamily: String?, val optionCode: String?, val searchPattern: String?, val outputValue: String)
val customizingProductOptions = mutableListOf(
CustomizingProductOption(10001, "Chimney", "Additional options", "^AAA$", "", "^Chimney with", "Available"),
CustomizingProductOption(10002, "Windows", "Basic options", "", "^222$", "^Two wide windows", "Available"),
CustomizingProductOption(10002, "Windows", "Basic options", "", "^555$", "wood windows$", "Available"),
CustomizingProductOption(10003, "Door color", "Basic options", "^CCCC$", "", "door in green color$", "green"),
CustomizingProductOption(10004, "House area", "Basic options", "^DDD", "", "120 square meters", "120")
)
The target is to check the product input data and to identify different product options. Whitin the following loop it is done by use of a business logic. There are 2 different constelations which can occure:
Option family + regex within option description
Option code + regex within option description
data class IndicatedOptions(val id: Int, val output: String)
val indicatedOptions: MutableList<IndicatedOptions> = mutableListOf()
for (i in 0 until inputProductData.size) {
for (k in 0 until customizingProductOptions.size) {
if(inputProductData[i].optionFamilyInput.toString().contains(Regex(customizingProductOptions[k].optionFamily.toString())) == true &&
inputProductData[i].optionDescriptionInput.toString().contains(Regex(customizingProductOptions[k].searchPattern.toString())) == true ||
inputProductData[i].optionCodeInput.toString().contains(Regex(customizingProductOptions[k].optionCode.toString())) == true &&
inputProductData[i].optionDescriptionInput.toString().contains(Regex(customizingProductOptions[k].searchPattern.toString())) == true) {
indicatedOptions.add(IndicatedOptions(customizingProductOptions[k].id, customizingProductOptions[k].outputValue))
}
}
}
println("\n--- ALL INDICATED OPTIONS ---")
indicatedOptions.forEach { println(it) }
val indicatedOptionsUnique = indicatedOptions.distinct().sortedBy { it.id }
println("\n--- UNIQUE INDICATED OPTIONS ---")
indicatedOptionsUnique.forEach {println(it)}
QUESTION: Do you see any ways to optimize this codein order to get it more faster?
First, the "regex" code looks broken. Why do you test if a String contains a Regex? This is the wrong way around you would normally test a Regex to see if the target string is matched by the Regex.
Ideas for performance
Precompile your Regex in the constructor of CustomizingProductOption
Your if logic is 4 logic ANDs. The code executes first to last in a logical expressions, so arrange the first test to be the one that is most selective (i.e. have the least number of matches).
Ideas for readability
use proper streams, e.g. inputProductData.map { customizingProductOptions.filter { LOGIC } }...
Stop using unnecessary toString() on something that is already a String
Stop testing if a boolean expression ==true
Now with sample code:
# Use Regex class here
data class CustomizingProductOption(
val id: Int, val optionName: String, val optionCategory: String,
val optionFamily: Regex?, val optionCode: Regex?, val searchPattern: String?,
val outputValue: String,
)
# Instantiate like this:
CustomizingProductOption(
10001, "Chimney", "Additional options", Regex("^AAA$"),
null, "^Chimney with", "Available",
),
# main code
val indicatedOptions: List<IndicatedOptions> = inputProductData.map { productData ->
customizingProductOptions.filter { option -> // this filter will only return matching options to product data
productData.optionFamilyInput != null && option.optionFamily?.containsMatchIn(productData.optionFamilyInput) ?: false
//&& other conditions
}
.map {option -> // transform to your desired output
IndicatedOptions(
option.id,
option.outputValue,
)
}
}.flatten() // you need this to flatten List<List<IndicatedOptions>>

How to handle null values in custom function?

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 ""

Split list when predicate is true

Does Kotlin provide a mutation function to split a list when a specific predicate is true?
In the following example the list should be split when the element is a ..
The result should be of the type List<List<String>>.
// input list
val list = listOf(
"This is", "the", "first sentence", ".",
"And", "now there is", "a second", "one", ".",
"Nice", "."
)
// the following should be the result of the transformation
listOf(
listOf("This is", "the", "first sentence"),
listOf("And", "now there is", "a second", "one"),
listOf("Nice")
)
I need something like list.splitWhen { it == "." }
Does Kotlin provide a mutation function to split a list when a
specific predicate is true?
The closest one I have heard of is partition(), however I don't think it will work in your case.
I have made and have briefly tested 3 higher order extension functions, which gives the same expected output.
Solution 1: Straightforward approach
inline fun List<String>.splitWhen(predicate: (String)->Boolean):List<List<String>> {
val list = mutableListOf<MutableList<String>>()
var needNewList = false
forEach {
string->
if(!predicate(string)){
if(needNewList||list.isEmpty()){
list.add(mutableListOf(string))
needNewList= false
}
else {
list.last().add(string)
}
}
else {
/* When a delimiter is found */
needNewList = true
}
}
return list
}
Solution 2: Pair based approach
inline fun List<String>.splitWhen(predicate: (String)->Boolean):List<List<String>> {
val list = mutableListOf<List<String>>()
withIndex()
.filter { indexedValue -> predicate(indexedValue.value) || indexedValue.index==0 || indexedValue.index==size-1} // Just getting the delimiters with their index; Include 0 and last -- so to not ignore it while pairing later on
.zipWithNext() // zip the IndexValue with the adjacent one so to later remove continuous delimiters; Example: Indices : 0,1,2,5,7 -> (0,1),(1,2),(2,5),(5,7)
.filter { pair-> pair.first.index + 1 != pair.second.index } // Getting rid of continuous delimiters; Example: (".",".") will be removed, where "." is the delimiter
.forEach{pair->
val startIndex = if(predicate(pair.first.value)) pair.first.index+1 else pair.first.index // Trying to not consider delimiters
val endIndex = if(!predicate(pair.second.value) && pair.second.index==size-1) pair.second.index+1 else pair.second.index // subList() endIndex is exclusive
list.add(subList(startIndex,endIndex)) // Adding the relevant sub-list
}
return list
}
Solution 3: Check next value if delimiter found approach
inline fun List<String>.splitWhen(predicate: (String)-> Boolean):List<List<String>> =
foldIndexed(mutableListOf<MutableList<String>>(),{index, list, string->
when {
predicate(string) -> if(index<size-1 && !predicate(get(index+1))) list.add(mutableListOf()) // Adds a new List within the output List; To prevent continuous delimiters -- !predicate(get(index+1))
list.isNotEmpty() -> list.last().add(string) // Just adding it to lastly added sub-list, as the string is not a delimiter
else -> list.add(mutableListOf(string)) // Happens for the first String
}
list})
Simply call list.splitWhen{it=="delimiter"}. Solution 3 looks more syntactic sugar. Apart from it, you can do some performance test to check which one performs well.
Note: I have done some brief tests which you can have a look via Kotlin Playground or via Github gist.

Getting a substring from a list of strings

So, as part of learning the language, I wanted to check three strings for a certain pattern and return the first match of that pattern only.
My attempt was to use a combination of find and regular expressions to traverse the list:
def date = [
"some string",
"some other string 11.11.2000",
"another one 20.10.1990"
].find { title ->
title =~ /\d{2}\.\d{2}\.\d{4}/
}
This kind of works, leaving the whole string in date.
My goal, however, would be to end up with "11.11.2000" in date; I assume somehow I should be able to access the capture group, but how?
If you want to return a specific value when finding a matching element in a collection (which as in your case might be part of that element), you need to use findResult.
Your code might then look like this
def date = [
"some string",
"some other string 11.11.2000",
"another one 20.10.1990"
].findResult { title ->
def res = title =~ /\d{2}\.\d{2}\.\d{4}/
if (res) {
return res[0]
}
}
Extending UnholySheep's answer, you can also do this:
assert [
"some string",
"some other string 11.11.2000",
"another one 20.10.1990"
].findResult { title ->
def matcher = title =~ /\d{2}\.\d{2}\.\d{4}/
matcher.find() ? matcher.group() : null
} == '11.11.2000'
For all matches, just use findResults instead of findResult, like this:
assert [
"some string",
"some other string 11.11.2000",
"another one 20.10.1990"
].findResults { title ->
def matcher = title =~ /\d{2}\.\d{2}\.\d{4}/
matcher.find() ? matcher.group() : null
} == ['11.11.2000', '20.10.1990']

Scala Map a list of items to a value

I have a list of bigrams of a sentence and another original list of relevantbigrams, I want to check that if any of the relevantbigrams are present in the sentences then I want to return the sentence. I was thinking of implementing it as follows: map each of the bigrams in the list to the sentence they come from then do a search on the key an return the value.
example:
relevantbigrams = (This is, is not, not what)
bigrams List(list(This of, of no, no the),list(not what, what is))
So each list is a bigram of separate sentences. Here "not what" from the second sentence matches, so I would like to return the second sentence. I am planning to have a map of Map("This of" -> "This of no the", "of no" ->"This of no the", "not what"->"not what is"). etc. and return the sentences that match on relevant bigram, so here I return "not what is"
This is my code:
val bigram = usableTweets.map(x =>Tokenize(x).sliding(2).flatMap{case Vector(x,y) => List(x+" "+y)}.map(z => z, x))
for(i<- 0 to relevantbigram.length)
if(bigram.contains(relevantbigram(i)))) bigram.get(relevantbigram(i))
else useableTweets.head
You got the order or flatMap and map the wrong way around:
val bigramMap = usableTweets.flatMap { x =>
x.split(" ").sliding(2).
map(bg => bg.mkString(" ") -> x)
} toMap
Then you can do your search like this:
relevantbigrams collect { rb if theMap contains rb => bigramMap(rb) }
Or
val found =
for {
rb <- relevantbigrams
sentence <- theMap get rb
} yield sentence
Both should give you a list, but from your code it appears you want to default to the first sentence if your search found nothing:
found.headOption.getOrElse(usableTweets.head)