Ternary operator in kotlin [duplicate] - android

This question already has answers here:
How to write ternary conditional operator?
(33 answers)
Closed 4 years ago.
I can write in java
int i = 10;
String s = i==10 ? "Ten" : "Empty";
Even I can pass it in method parameter.
callSomeMethod(i==10 ? "Ten" : "Empty");
How do I convert it to kotlin? Lint shows error when writing same thing in kotlin.

callSomeMethod( if (i==10) "Ten" else "Empty")
Discussion about ternary operator:
https://discuss.kotlinlang.org/t/ternary-operator/2116/3

Instead of
String s = i==10 ? "Ten" : "Empty";
Technically you can do
val s = if(i == 10) "Ten" else "Empty"
val s = when {
i == 10 -> "Ten"
else -> "Empty"
}
val s = i.takeIf { it == 10 }?.let { "Ten" } ?: "Empty"
// not really recommended, just writing code at this point
val s = choose("Ten", "Empty") { i == 10 }
inline fun <T> choose(valueIfTrue: T, valueIfFalse: T, predicate: () -> Boolean) =
if(predicate()) valueIfTrue else valueIfFalse

You can create an extension function with generic for boolean value
fun <T> Boolean.elvis( a :T, b :T ): T{
if(this) // this here refers to the boolean result
return a
else
return b
}
and now you can use it for any boolean value (cool Kotlin)
// output
System.out.print((9>6).elvis("foo","bar")) foo
System.out.print((5>6).elvis("foo","bar")) bar
Extensions in kotlin

As the original question was using the term 'Elvis operator' it may be a good idea to provide a short comparison with the ternary operator.
The main difference between ternary operator and Elvis operator is that ternary is used for a short if/else replacement while the Elvis operator is used for null safety, e.g.:
port = (config.port != null) ? config.port : 80; - a shortcut for an if/then/else statement
port = config.port ?: 80 - provides a default value to be used in case the original one is null
The examples look very similar but the Ternary operator can be used with any type of boolean check while the Elvis operator is a shorthand for use config.port if it's not null else use 80 so it only checks for null.
Now, I think Kotlin doesn't have a ternary operator and you should use an if statement like so - s = if (i==10) "Ten" else "Empty"

Related

I use proper way to compare two strings but still the program is malfunctioning [duplicate]

I'm studying kotlin, but I'm very disappointed, I can not compare two Strings.
What is the right way to compare.
btn_login.setOnClickListener {
val login = input_email.text.trim()
val pass = input_password.text.trim()
if( login.equals( pass ) ){
startActivity<MainActivity>()
}
if (login?.equals(other = pass)){
startActivity<MainActivity>()
}
if (login == pass){
startActivity<MainActivity>()
}
}
According to documentation for structual equality use ==. It is translated to a?.equals(b) ?: (b === null).
In you case convert login and pass from SpannableStringBuilder to String.
val login = input_email.text.trim().toString()
Here is the example for matching the two strings using kotlin.
If you are using == (double equals) for matching the string then it's compare the address & return maximum time wrong result as per java documentation so use equals for the same
If you want to use equal ignore case then pass the true in the equals method of String
if (s1.equals(s2,true))
other wise you can just use this without boolean like
if (s1.equals(s2,false)) or if (s1.equals(s2))
compleate code is below
fun main(args: Array<String>) {
val s1 = "abc"
val s2 = "Abc"
if (s1.equals(s2,true))
{
println("Equal")
}
else
{
println("Not Equal")
}
}
Covert both the SpannableStringBuilder to string with toString, this should work.
val login = input_email.text.trim().toString()
val pass = input_password.text.trim().toString()
if (login == pass){
startActivity<MainActivity>()
}
1. == :
if ( string1 == string2 ){...}
2. equals :
Indicates whether some other object is "equal to" this one.
Implementations must fulfil the following requirements:
Reflexive: for any non-null reference value x, x.equals(x) should
return true.
Symmetric: for any non-null reference values x and y, x.equals(y)
should return true if and only if y.equals(x) returns true.
Transitive: for any non-null reference values x, y, and z, if
x.equals(y) returns true and y.equals(z) returns true, then
x.equals(z) should return true
Consistent: for any non-null reference values x and y, multiple
invocations of x.equals(y) consistently return true or consistently
return false, provided no information used in equals comparisons on
the objects is modified.
/** * Returns `true` if this string is equal to [other], optionally ignoring character case. * * #param ignoreCase `true` to ignore character case when comparing strings. By default `false`. */
public fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean
3. compareTo :
public override fun compareTo(other: String): Int
Compares this object with the specified object for order. Returns zero
if this object is equal to the specified other object, a negative
number if it's less than other, or a positive number if it's greater
than other.
public fun String.compareTo(other: String, ignoreCase: Boolean = false): Int
Compares two strings lexicographically, optionally ignoring case
differences
i know this is way too late, but as a newbie learning Kotlin, i had the same doubts.
then i came across this wonderful article that articulates the various string comparison types in Kotlin and the differences between them all.
in short both == and .equals() can be used to compare the value of 2 strings in kotlin.
hopefully that helps
With case checking
String a=.....
String b=.....
if(a==b){
}
IgnoreCase
if(a.equals(b,false))
KOTLIN:
if (editText1.text.toString() == editText2.text.toString() ) {
println("Should work now! The same value")
}
Try the following solution, see if it helps:
val passStr: String = textView.text.toString()
if( loginStr.compareTo(passStr, false) ){
startActivity<MainActivity>()
}
Try this surely will work.
val style = buildString { karthik}
val style2 = buildString { karthik }
var result = style.equals(style2)
if(result){//Do something}

Why getting a type mismatch when using Collections.Sort method with a List<String?>?

Why the following code failed to compile with the error:
"Type mismatch: inferred type is String? but Comparable<String?>! was expected
What does it mean the '!' after the brackets? And why it says that infered type is String
if it defiently MutableList<String?>? ?
The code:
private fun createAllGamesList(gamesPerCountriesMap: MutableMap<String?, MutableMap<Long, MutableList<AllScoresGameObj>>>?)
:List<BaseDataItem>
{
var countriesNameSet = gamesPerCountriesMap?.keys
var countriesNameList = countriesNameSet?.toMutableList()
Collections.sort(countriesNameList)
countriesNameList?.let{
}
}
Just want to point out that using the Java class Collections in Kotlin is kind of discouraged since the Kotlin standard library has built-in functions that can do the same tasks in ways that fit Kotlin-style syntax better, has better nullability handling, and can be more performant when they use inline lambdas.
For instance, you wouldn't be running into the weird type Comparable<String?>! if using Kotlin's sort() or sortedBy(). The ! symbol after a type means it's maybe nullable and maybe not because the Java code doesn't specify either way.
In Kotlin, you can call sort() directly on your MutableList (it's an extension function) if it has a Comparable type. You'll still run into the problem that String? is not a Comparable<String?>, but you can do custom sorting using compareBy and thenBy to create a Comparator:
countriesNameList.sortedWith(
compareBy<String?> { it ?: "" } // lambda returns String, which is Comparable
.thenBy { it == null } // Sort between empty Strings and null values by returning a Boolean (another Comparable
)
The problem is that countriesNameList is a list of nullable Strings. That is, its type is MutableList<String?>, and not MutableList<String>.
Collections.sort() requires that the collection's type implement Comparable<T>, but String? does not implement Comparable<String?>. I imagine this is because it is not obvious where null values would fall in the natural ordering of String? elements.
This is very easy to observe with the following code:
val list: MutableList<String?> = mutableListOf("hello", "world")
Collections.sort(list)
If I change the type specification to not include the ?, the problem goes away:
val list: MutableList<String> = mutableListOf("hello", "world")
Collections.sort(list)
You can work around this by providing your own comparator:
Collections.sort(countriesNameList) { lhs, rhs -> customCompare(lhs, rhs) }
private fun customCompare(lhs: String?, rhs: String?): Int = when {
lhs == null && rhs == null -> 0
lhs == null -> 1
rhs == null -> -1
else -> lhs.compareTo(rhs)
}

How to sort a collection by multiple values AND nulls at the end using the available convenient methods of Kotlin

I've used the sortedWith and compareBy methods before and it is capable of sorting using multiple parameters.
I've also used nullsLast method before and it is quite convenient just like the other two.
With these methods, however, I can't figure out a way to sort a collection with the following sorting rules:
Given the Class:
data class MyClass(
val varA: String?,
val varB: String
)
sort according to varA alphabetically and nulls/blank to be last; then
sort according to varB alphabetically
So let's say I have the following collection:
val collection = listOf(
MyClass(null, "A"),
MyClass("a", "B"),
MyClass("c", "C"),
MyClass("b", "D"),
MyClass("", "E"),
MyClass("a", "F"),
MyClass("b", "G"),
MyClass(null, "H"),
MyClass("", "I")
)
it should then be sorted to:
MyClass("a", "B")
MyClass("a", "F")
MyClass("b", "D")
MyClass("b", "G")
MyClass("c", "C")
MyClass(null, "A")
MyClass("", "E")
MyClass(null, "H")
MyClass("", "I")
is there a one-liner code I can use just like the compareBy that uses vararg parameter
I would do it like this (see kotlin.comparisons documentation):
val comparator = compareBy<String, MyClass>(nullsLast(), { it.varA.let { if (it == "") null else it } }).thenBy { it.varB }
collection.sortedWith(comparator)
Of course there is a one-liner for this:
collection.sortedBy {if(it.varA != null && it.varA.isNotEmpty()) it.varA + it.varB else "z" + it.varB}
Then print it:
output.forEach { println("Myclass(${it.varA}, ${it.varB})") }
Output:
Myclass(a, B)
Myclass(a, F)
Myclass(b, D)
Myclass(b, G)
Myclass(c, C)
Myclass(null, A)
Myclass(, E)
Myclass(null, H)
Myclass(, I)
Explanation:
We need to split the cases when we sort by varA and when by varB here. That is why there is a condition if(it.varA != null && it.varA.isNotEmpty()), which says that we will sort by varA only if its value isn't null or "".
Otherwise, we sort by string "z" + it.varB, which means that we sort by varB, but with z prefix, which will ensure that these items will be at the end of sorted collection.
To understand more easily how it works, you can try following code:
val output = collection.map { if(it.varA != null && it.varA.isNotEmpty()) it.varA + it.varB else "z" + it.varB }
output.sortedBy { it }.forEach { println(it) }
This one will output these strings:
aB
aF
bD
bG
cC
zA
zE
zH
zI
Now it should be more obvious. :-)

Tertiary operator in kotlin [duplicate]

This question already has answers here:
How to write ternary conditional operator?
(33 answers)
Closed 3 years ago.
How do we use ternary operator in Kotlin?
I have tried using ternary operator in same manner that we use in java but I got a lint error in it:
var myVariable = (condition == true) ? value1 : value2
// Valid Kotlin, but invalid Java/C#/JavaScript
var v = if (a) b else c
Alternative:
when(a) {
true -> b
false -> c
}
Hope this helps. Good luck.

Does when comparison ignores case of String?

I have a method which takes String as input and returns an Integer corresponding to it as shown below :
fun getPriority(groupValue: String?): Int {
when (groupValue) {
"one" -> return 10
"Two" -> return 9
"THREE" -> return 8
else -> return 4
}
}
My Question is String comparison in this case takes case of string into consideration or ignores case?
when does a equals-comparison, so it is case-sensitive indeed (see also String.equals).
Ignoring case sensitivity can be accomplished in several ways, one of which is already shown by Willi Mentzel... Some other (depending on what you want to accomplish):
fun getPriority(groupValue : String?) = when {
groupValue == null -> /* handle the null first, so you can concentrate null-safe on the rest later */ 4
groupValue.equals("one", ignoreCase = true) -> 10 /* ignoreCase = false is the default */
/* ... */
else -> 4
}
If it's that simple Willis approach will probably suffice you already.
As other answers has stated, the when-expression uses the equals method for comparison. The equals method on String is case sensitive by default.
If you want to compare objects on something else than its equals method, you could create a small wrapper class with it's own implementation of equals. This might be a little overkill in your particular case, but it might be useful in other cases.
The wrapper class:
// In Kotlin 1.3, this class could probably be inlined.
class CaseInsensitiveString(val s: String) {
fun equals(other: Any) =
(other as? CaseInsensitiveString)?.s?.equals(s, ignoreCase = true) ?: false
// Overriding hashCode() just so it's consistent with the equals method.
override fun hashCode() = s.toLowerCase().hashCode()
}
A convenient extension property to wrap a String:
val String.caseInsensitive
get() = CaseInsensitiveString(this)
Now you can do this:
fun getPriority(groupValue: String?): Int {
when (groupValue?.caseInsensitive) {
"one".caseInsensitive -> return 10
"Two".caseInsensitive -> return 9
"THREE".caseInsensitive -> return 8
else -> return 4
}
}
Yes, it is case sensitive, because String.equals is invoked as Roland already said.
To make it case insensitive:
fun getPriority(groupValue: String?) = when (groupValue?.toLowerCase()) {
"one" -> 10
"two" -> 9
"three" -> 8
else -> 4
}
Tip: since when is an expression, you can use the expression body notation for your function.
Alternative:
Use a Map instead of when. This becomes especially handy if your code should be dynamic.
val priorityMapping = mapOf(
"one" to 10,
"Two" to 9,
"THREE" to 8
)
fun getPriority(groupValue: String?): Int {
groupValue?.let {
priorityMapping.forEach { (key, value) ->
if(key.equals(it, true)) {
return value
}
}
}
return 4 // default value
}
As others already stated, when uses equals to form a boolean expression if a scalar (or a list of scalars) is placed in front of the -> sign. Therefore (snippet from official docs of when - block comments are mine):
when (x) {
1 -> print("x == 1") /* same as x.equals(1) or x == 1 */
2 -> print("x == 2") /* same as x.equals(2) or x == 2 */
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
According to the doc of text.equals:
Parameters
ignoreCase - true to ignore character case when comparing characters. By default
false.
Two characters are considered the same ignoring case if at least one
of the following is true:
The two characters are the same (as compared by the == operator)
Applying the method toUpperCase to each character produces the same result
Applying the method toLowerCase to each character produces the same result
So in you case groupValue.equals("one") is the same as groupValue.equals("one", ignoreCase = false) therefore the answer is yes, when you use when on a String by default the comparison will consider casing.

Categories

Resources