How to find specific keyword in a string? - kotlin

I have following code:
val contains = jobAd.toString().lowercase()
.contains(keyword.lowercase())
Problem is its getting hit even the string have "javascript". But i only want it to behit when it's actually the word java.
What am i doing wrong?

If you want to match only an entire word, you can use word boundaries in a regular expression like this:
fun String.findWord(word: String) = "\\b$word\\b".toRegex().containsMatchIn(this)
Then you can write for instance:
"I like Java!".findWord("Java") // true
"I like JavaScript".findWord("Java") // false
Note that this is case sensitive and not very robust, because for instance, it is possible to inject a regular expression. This is just to give you the general idea.

Related

Kotlin - Is it possible to check for operator precedence

Let's say I have the following class:
data class Foo(var name: String) {
operator fun plus(foo: Foo): Foo {
name += foo.name
return this
}
}
Which is then used like this:
val foo1 = Foo("1")
val foo2 = Foo("2")
val foo3 = Foo("3")
foo1+foo2+foo3
println(foo1.name) // 123
Now, what if I wanted different behavior depending on whether the operations are chained like this:
foo1+foo2+foo3
Or like this:
(foo1+foo2)+foo3
In both cases foo1's name would be 123, but let's say that in the second case I would want foo1's name to be (12)3.
Is there a way to add a condition to the plus function, which checks whether the foo that it is called on originates from within parentheses/has a higher precedence or not.
No, that is not possible, because that makes no sense tbh. The compiler will just resolve the order of operations, brackets just indicate that 1+2 should resolve first and the result should be added to 3. There is no concept of brackets anymore in that result, you just have the outcome.
What is confusing you is that you are abusing the plus function to do something people wouldn't expect. You should not use the plus function to mutate the object it is called upon, this is not expected behaviour. Users will expect the plus function to return a new object not a mutation of the left or right operand.
In your case:
operator fun plus(foo: Foo): Foo {
return Foo(name += foo.name)
}
Don't do something different lest you want other people to be really confused. Fyi plusAssign is a mutating function, but still wouldn't allow you to do what you want. To achieve that you'd probably need to write your own parser and parse the operands and operators yourself.

How does this extension property to capitalize the first letter of every word in a string work?

I'm recently trying to get into kotlin (coming from java) and did some beginner coding challenges.
The task is to write a function that capitalizes the first letter of every word in a string
I could solve the task in my own way, but I still don't understand this sample solution:
fun capitalizeSentence(str: String) {
println(str.split(" ").joinToString(" ") { it.capitalize() })
}
Can someone explain to me, why this lambda expressions manages to capitalize the first letter of every word, even when using the joinToString method right after the split method?
joinToString() accepts an optional transform function which it applies to each item before joining. It might be confusing to you, because this code performs operations in a different order than the code flow itself. This code is effectively an equivalent of this one:
str.split(" ")
.map { it.capitalize() }
.joinToString(" ")

How can I use Mono<Boolean> as condition to call second method

I'm trying to make a call to one service after checking a condition from another service in an iterative way, like so:
if (productService.isProductNotExcluded(product)){
List<Properties> properties = propertiesService.getProductDetailProperties(product)
...
}
But since isProductExcluded is returning Mono<Boolean> I'm using this approach, which seems really odd:
Flux<Properties> properties = productService.isProductNotExcluded(productId)
.filter(notExcluded -> notExcluded)
.map(ok-> propertiesService.getProductDetailProperties(product))
...
Which is the correct way to deal with this kind of situation?
For a predicate which returns a Mono<Boolean>, you can also use filterWhen which takes a publisher as a predicate. Something like this:
Flux<Properties> properties = Mono.just(productId)
.filterWhen(prodId -> productService.isProductNotExcluded(prodId))
.map(validProductId -> propertiesService.getProductDetailProperties(validProductId));
What you are doing is not odd. I personally wouldn't return a boolean in a reactive function Mono<Boolean> if I can avoid it, but it's not wrong and sometimes you don't have a choice.
I personally would have an if/else statement in the map, for clarity. I would also change the name of the function, and rewrite the isNot part.
Flux<Properties> properties = productService.isExcluded(productId)
.flatMap(isExcluded -> {
if(!isExcluded)
return propertiesService.getProductDetailProperties(product);
else
return mono.empty();
});
This is matter of opinion and coding taste, but I find this to be a lot more readable, because you can read the code and understand it straight away. But this is a personal taste.
all() operator can be used.
According to the doc. all() Emits a single boolean true if all values of this sequence match
Mono all(Predicate<? super T> predicate) {}

How do I replace multiple characters in a String?

How do I replace multiple characters in a String?
Like Java's replaceAll(regex:replacement:) function.
str.replaceAll("[$,.]", "") //java code
This answer is very close but I want to change more than one character at the same time.
[$,.] is regex, which is the expected input for Java's replaceAll() method. Kotlin, however, has a class called Regex, and string.replace() is overloaded to take either a String or a Regex argument.
So you have to call .toRegex() explicitly, otherwise it thinks you want to replace the String literal [$,.]. It's also worth mentioning that $ in Kotlin is used with String templates, meaning in regular strings you have to escape it using a backslash. Kotlin supports raw Strings (marked by three " instead of one) which don't need to have these escaped, meaning you can do this:
str = str.replace("""[$,.]""".toRegex(), "")
In general, you need a Regex object. Aside using toRegex() (which may or may not be syntactical sugar), you can also create a Regex object by using the constructor for the class:
str = str.replace(Regex("""[$,.]"""), "")
Both these signal that your string is regex, and makes sure the right replace() is used.
If you're happy to work with regular expressions, then refer to the accepted answer here. If you're curious as to how you can achieve this without regular expressions, continue reading.
You can use the String.filterNot(predicate:) and Set.contains(element:) functions to define a String.removeAll extension function as follows:
/**
* #param charactersToRemove The characters to remove from the receiving String.
* #return A copy of the receiving String with the characters in `charactersToRemove` removed.
*/
fun String.removeAll(charactersToRemove: Set<Char>): String {
return filterNot { charactersToRemove.contains(it) }
}
You would call on this function as follows: myString.removeAll(setOf('$', '.'))

String template to set the default value of PARAMETER

Is it possible, in ABAP, to evaluate string templates dynamically?
Normally, you will have some string template in code that will be checked by the compiler. (The variables in the curly brackets are checked by the compiler at compile time).
However, is it possible to have a string evaluated at runtime?
So, instead of:
data(val) = |System ID: { sy-sysid }|.
I would like the string to be interpolated to come from elsewhere, for example:
parameter: p_file type string lower case default '/mnt/{ sy-sysid }/file.txt'.
In this case, I would like to have the value of p_file to be evaluated at runtime to substitute the variable (sy-sysid) with the runtime value.
You could, of course, program your own substitution by finding all occurrences of variables with curly brackets with a regex expression, then evaluate the variable values with ASSIGN and substitute them back into the string, but I am looking for a built-in way to do this.
Sorry, this is maybe a stupid example, but hopefully you understand what I mean. (If not, please let me know in the comments and I will try and clarify).
The problem in your snippet is not with string template but with PARAMETER behavior. It does not allow dynamics in DEFAULT clause.
To achieve what you want you should use INITIALIZATION and set path value in runtime:
parameter: p_file type string lower case.
INITIALIZATION.
p_file = | /mnt/{ sy-sysid }/file.txt |.
Unfortunately, the example you gave, does not make any sense to me. ABAP String templates are evaluated at run-time and type-checked at compile-time.
In your example, it is always the run-time value of SY-SYSID that will be written to the variable.
I guess what you want to do is circumvent compile-time checks for expressions inside a string template.
Please try to give us your actual use case, so maybe we find an even better solution to your problem.
However, here is what I think could help you:
Personally, I do not recommend to write code like the one below, because it is extremely error-prone likely to mislead other programmers and because there is very likely a better solution.
Given that you know the name of a variable at run-time, try this:
".. say LV_VARNAME is a charlike variable that contains a
" variable name at runtime.
"NOTE that the variable LV_VARNAME must be visible in the scope of the
"following code.
FIELD-SYMBOLS: <my_var> TYPE any.
ASSIGN (lv_varname) TO <my_var>.
DATA(lv_str) = |The value is { <my_var> }|.