null safety without elvis operator kotlin - kotlin

a ?: b is just shorthand for if (a != null) a else b.
I understand that ?: eliminates the danger of null references from code. However I encountered fragment, which I failed to re-write myself. Here it is:
`
val words = mutableMapOf<String, Int>()
input = scan.next()
wordCounts[input] = (wordCounts[input] ?: 0) + 1
What I tried is:
words[input] = if (words[input] != null) (words[input]+1) else 0
However Intelij displays error here
After I replaced it with its sugesttion I have further errors:
words[input] = if (words[input] != null) (words[input]?.plus(1)) else 0
How can I fix that if statement remain?

val n = words[input]
words[input] = if (n != null) n + 1 else 0
words might change value to become null after your if statement, so you should capture the variable first and then do the assignment.

a ?: b is just shorthand for if (a != null) a else b
That's almost true.  But there's one subtle difference: the elvis version only evaluates a once, while the other evaluates it twice — carrying the possibility that it could give a different result each time.
In this case, a is words[input].  What if another thread changes the contents of words in between the two evaluations, removing the value for key input so that words[input] returns null the second time?  (Even if you know that no other thread could possibly be accessing it, if the compiler can't prove that, it has to assume that the second time could give a null result.)
So the solution is to evaluate it only once.
You can do that with a temporary variable, as per another answer.  But Kotlin's scoping functions give you a slightly simpler option:
words[input].let{ if (it != null) it + 1 else 1 }
This works because let() is an extension method defined on Any?, so (unlike ‘real’ methods) it's safe to call on a null.
However, this just illustrates why the elvis operator is useful, and how it simplifies some code!  You don't explain why you want to rewrite your code to avoid it; that could be a useful learning exercise, but in practice you'd just use the elvis operator instead.

Related

Kotlin: 'if' must have both main and 'else' branches if used as an expression

I am trying to run this code:
fun main() {
print("This is a calculator")
print("enter your first number")
val no1 = readLine()!!
print("enter your operation")
val operation1 = readLine()!!
print("enter your second number")
val no2 = readLine()!!
val result = if(operation1 == '*')
print("$val no1 * $val no2")
}
But I get the following error:
'if' must have both main and 'else' branches if used as an expression
Operator '==' cannot be applied to 'String' and 'Char' Keyword cannot
be used as a reference Keyword cannot be used as a reference
It also keeps coming up with Kotlin: Keyword cannot be used as a reference within my IDE.
I am very new to Kotlin and I am just trying to create a basic calculator, can you please help?
That code has three different compilation errors, and many other things to fix:
In the line:
print("$val no1 * $val no2")
the $val is a string template which would fill in the value of the variable val — but val isn't a variable; it's a keyword. (That's what's causing the ‘Keyword cannot be used as a reference’ error.) I think you want $no1 and $no2.
The test if(operation1 == '*') is comparing a string with a character. (Single-quotes give a character constant. A string contains characters, but it is not a character, so it can never be equal to a character.) The compiler tells you this with the error ‘Operator '==' cannot be applied to 'String' and 'Char'’. The simplest way to fix it is to provide a string by using double-quotes instead: "*".
The pair of lines:
val result = if(operation1 == '*')
print("$val no1 * $val no2")
is incomplete and doesn't make sense as it is. What do you want to do if operation1 isn't "*"? And what do you want to set result to in each case?
(It looks like you're trying to use if as an expression, and assign its result to result — but to use if as an expression you need to add an else branch too, so it knows what value to use in all cases. That's causing the compilation error ‘'if' must have both main and 'else' branches if used as an expression’.)
If this is the start of some code which will check for *, +, and other operators, then maybe you want to set the result and then print it, e.g.:
val result = if (operation1 == "*") /* … */
else if (operation2 == "+") /* … */
else /* … */
print(result)
(Though that would be simpler with when instead of if.)
If you fix the first issue by changing it to "$no1 * $no2", that will print e.g. 1 * 2. I'm guessing you want instead to calculate the product of two numbers. But no1 and no2 aren't numbers; they're strings. So you first have to convert them into numbers, e.g. with no1.toInt(). You also have to tell the compiler that you want to evaluate the * rather than just printing it. If you just want to print out the number, you could skip the string template entirely:
print(no1.toInt() * no2.toInt())
However, if you want to use a string template (e.g. if you want to add some text), you'd have to use the ${…} format to mark out the bit you want to evaluate:
print("${no1.toInt() * no2.toInt()}")
Some or all of the print(…) calls should probably be println(…). (println() adds a line-break after the string, while print() doesn't, leaving the next input or output to follow immediately on the same line.)
Even if all that's fixed, the code could fail in several ways if the user doesn't enter the expected input. (Both !! and toInt() can throw exceptions, e.g. if you press Enter without typing a number.) That's fine for an exercise, of course; but if this code ever got to production, you'd want to handle those possibilities better.
When you use $val, it is trying to access the val variable, but as $val is undefined, it can't, and freaks out because it doesn't know what to do.
Instead of print("$val no1 * $val no2"), you can use:
print("$no1 * $no2")
The code above will use the $no1 and $no2 variables that you want it to use.
In line 4 and 8, the input should be converted to Int, like this:
val no1 = readLine()!!.toInt()
val no2 = readLine()!!.toInt()
In the last line, val should not be written and should be as follows:
print("$no1 * $no2")
You can also add other operators using else if in the continuation of the if command

Java8 time comparison in Kotlin

I stumbled across some strange behaviour when comparing Java8 time objects.
The below does not appear to be valid code.
val t1 = LocalTime.now()
val t2 = LocalTime.now()
val foo: Int = t1 > t2
Yet hovering over the undersquiggled code shows that the overridden function return type is correct:
Any ideas?
According to the documentation, when using the natively overloaded operators (comparison operators specifically) - Kotlin doesn't just call compareTo but rather performs a compareTo against 0 (thus ending up as a bool).
https://kotlinlang.org/docs/operator-overloading.html#comparison-operators
Comparison operators
Expression
Translated to
a > b
a.compareTo(b) > 0
a < b
a.compareTo(b) < 0
a >= b
a.compareTo(b) >= 0
a <= b
a.compareTo(b) <= 0
All comparisons are translated into calls to compareTo, that is required to return Int.
The documentation snippet you attached is admittedly a bit confusing.

typeorm: how to properly use IsNotNull/IsNull?

We created a helper function to create wheres easier. It works fine with eq, neq, lt and gt. Now we're trying to add is null/is not null (for a date column, not sure if that matters).
The critical part of the function looks like this:
// This is ran in a loop for every attribute
const query = `${attribute}` ${comparator} :value${index}`;
// if the checked 'value' is NULL then use IsNull(), same for NOT NULL, otherwise simply use value
const params = { [`value${index}`]: value == 'NULL' ? IsNull() : value === 'NOT NULL' ? Not(IsNull()) : value};
// Add this (sub)query to the qb
qb.andWhere(query, params);
Now we get an error saying this:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near ’_type = ‘not’, _value = ‘[object Object]‘, _useParameter =
true, `_multipl’ at line 1"
Value is [object Object] - which kind of makes sense if we use IsNotNull(), right?
As far as I understand from this comment, IsNull() and Not(IsNull()) should work like we are trying to.
We use #nestjs/typeorm 7.1.5.
To check for NULL you need
qb.andWhere(`${attribute} IS NULL`)
To check for NOT NULL you need
qb.andWhere(`${attribute} IS NOT NULL`)
(Note: Omit the second argument, parameters, for these cases).
From your code seems you are using string values 'NULL' and 'NOT NULL' as the value arguments and checking these as special cases. Your code will now look like this:
if ((value == 'NULL' && comparator == '=') || (value == 'NOT NULL' && comparator == '<>'))
qb.andWhere(`${attribute} IS NULL`);
if ((value == 'NOT NULL' && comparator == '=') || (value == 'NULL' && comparator == '<>'))
qb.andWhere(`${attribute} IS NOT NULL`);
else
qb.andWhere(`${attribute} ${comparator} :value${index}`, { [`value${index}`]: value});
(In the code above I check for '=' and '<>' which are standard SQL comparison operators. If your SQL dialect uses 'eq' and 'ne' in place of '=' and '<>', which you mention in your question, you will need to change the code above. If so please update your question and add the appropriate tag to say which SQL database you are using).
When you test this, I recommend that you turn on TypeOrm full logging so you can see the actual generated SQL and you be able to quickly solve any problems. See TypeOrm logging.

use associate array total value count Lua

i want to count the data type of each redis key, I write following code, but run error, how to fix it?
local detail = {}
detail.hash = 0
detail.set = 0
detail.string = 0
local match = redis.call('KEYS','*')
for i,v in ipairs(match) do
local val = redis.call('TYPE',v)
detail.val = detail.val + 1
end
return detail
(error) ERR Error running script (call to f_29ae9e57b4b82e2ae1d5020e418f04fcc98ebef4): #user_script:10: user_script:10: attempt to perform arithmetic on field 'val' (a nil value)
The error tells you that detail.val is nil. That means that there is no table value for key "val". Hence you are not allowed to do any arithmetic operations on it.
Problem a)
detail.val is syntactic sugar for detail["val"]. So if you expect val to be a string the correct way to use it as a table key is detail[val].
Possible problem b)
Doing a quick research I found that this redis call might return a table, not a string. So if detail[val] doesn't work check val's type.

Using a table for variable name in a table is not found when called for

I am making quite the complex thing and I am trying to use tables as variable names cause I have found that lua works with it, that is:
lua
{[{1,2}]="Meep"}
The issue is it is callable, when I do it and try to call it using the same kind of table, it won't find it.
I have tried looking for it and such but I have no clue why it won't do this.
ua
local c = {[{1,2}]="Meep"}
print(c[{1,2}],c)
Do I expect to become but it does not.
"Meep",{[{1,2}]="Meep"}
but what I get is
nil,{[{1,2}]="Meep"}
If I however try
lua
local m={1,2}
local c = {[m]="Meep"}
print(c[m],c)
it becomes the correct one, is there a way to avoid that middle man? After all m=={1,2} will return true.
The problem you have is that tables in lua are represented as references. If you compare two different talbes you are comparing those references. So the equation only gets true if the given tables are exactly the same.
t = { 1, 2, 3 }
t2 = { 1, 2, 3 }
print(t == t) -- true
print(t2 == t) -- false
print(t2 == t2) -- true
Because of this fact, you can pass them in function per reference.
function f(t)
t[1] = 5
end
t2 = { 1 }
f(t2)
print(t2[1]) -- 5
To bypass this behavior, you could (like suggested in comments) serialize the table before using it as a key.