Is it possible to test a String for not being equal in a when-statement?
This is of course perfectly possible with a simpel if statement:
val storedValue = sharedPreferences.getString(identifier, NOT_SET)
if (storedValue != NOT_SET) {
super.setValue(storedValue)
}
However, I like how storedValue is scoped inside of the when-statement in this snippet:
when (val storedValue = sharedPreferences.getString(identifier, NOT_SET)) {
NOT_SET -> {}
else -> super.setValue(storedValue)
}
The downfall is the empty code block for the NOT_SET entry.
Is it possible to combine these two?
I'd like to scope storedValue and get rid of empty code blocks. The result would be comparable to:
when (val storedValue = sharedPreferences.getString(identifier, NOT_SET)) {
!NOT_SET -> super.setValue(storedValue)
}
Since SharedPreferences is part of the Android framework, this would another solution:
if (sharedPreferences.contains(identifier)) {
super.setValue(sharedPreferences.getString(identifier, null))
}
However, the goal of my question is deeper understanding of the possibilities of Kotlin, for the sake of learning.
As mentioned in the comments, negation is not directly supported like this in when statements (yet) even in Kotlin.
The most idiomatic way at the moment most probably is like:
val storedValue = sharedPreferences.getString(identifier, NOT_SET)
when {
storedValue != "NOT_SET" -> super.setValue(storedValue)
}
Another working variant utilizing !in in when could be for example:
when (val storedValue = sharedPreferences.getString(identifier, NOT_SET)) {
!in setOf("NOT_SET") -> super.setValue(storedValue)
}
And as both != and!in will compare case sensitively, so would make sense to get the string like sharedPreferences.getString(identifier, NOT_SET).toUpperCase(), or use equalsIgnoreCase in the first variant.
Related
There's gotta be a more Kotlin-esque and terse way to do this in Kotlin, but it's not coming to me. Imagine you're doing a mapNotNull operation. Items which cannot be mapped are converted to null to be filtered out. Items that cannot be mapped also result in a warning being printed. This code works, but it's really verbose. Can you help me trim it down?
val listOfStrings = listOf("1","2","3","not an int", "4","5")
val convertedToInts = listOfStrings.mapNotNull {
val converted = it.toIntOrNull()
if(converted == null){
println("warning, cannot convert '$it' to an int")
}
converted
}
I think your code is idiomatic and readable as it is. I prefer to write it with the explicit null-check.
But if you really want to make a shorter one-liner, you could do something like below. But it looks very hacky with the null.apply {} which is needed to return null instead of Unit from the right side of the elvis-operator:
val listOfStrings = listOf("1","2","3","not an int", "4","5")
val convertedToInts: List<Int> = listOfStrings.mapNotNull {
it.toIntOrNull()
?: null.apply { println("warning, cannot convert '$it' to an int") }
}
You could also use run which looks a bit more readable:
?: run {
println("warning, cannot convert '$it' to an int")
null
}
Kotlin noob here, I'm trying to update the element in listFirst by comparing some attribute (say, topicId) of the elements from the listSecond, both list contains the same type of element. I had the following code but it looks ugly to me. Is there anyway to make it look better or more efficient in Kotlin? Thank you! (The compiler version: java 8)
...
outputList = mutableListOf<MyObject>()
listFirst.forEach {
element1 ->
run {
val t = listSecond.firstOrNull { element1.topicId == it.topicId }
if (t != null) {
outputList.add(t)
} else {
outputList.add(element1)
}
}
}
return outputList
The run enclosure in your code is not being used for anything so you might as well remove it. Anyway, you can use map and an elvis operator to reduce your code quite a bit:
return listFirst.map { element1 ->
listSecond.firstOrNull { element1.topicId == it.topicId } ?: element1
}
If these are long lists, it would be more efficient to create a Map out of the second list so you aren't having to iterate it repeatedly with firstOrNull. This would change it from O(n^2) to O(n).
val mapSecond = listSecond.associateBy { it.topicId }
return listFirst.map { element1 ->
mapSecond[element1.topicId] ?: element1
}
I want to make an extension function(named (printDatatype) for example) that can be applied on all datatypes and just prints it ... for example :
"example".printDatatype() ==>output: example
56.prinDatatype()==>output: 56
null.printDatatype()==>output: null
className.printDatatype()==> output: ClassName(property1 = value, property2 = value ....)
Soo something like this?
fun Any?.printLn() = println(this)
Any custom objects will need to override the toString method (like always). That will be auto on data classes.
I honestly don't know what the use case for something like would be.
so i figured it out :)
fun Any?.printMe() {
if (this is Class<*>) {
println(this.toString())
} else if (this.toString() == "null"){
println("null")
}
else {
println(this)
}
}
1.Can I simplify the code in when statement to one or two lines.I am trying to replace the code in the when block but unable to do it.
// Loads all the settings changed in the theme
override fun onCreate() {
super.onCreate()
var sharedPreferences = getSharedPreferences(
CatalystConstants.keyThemeObject,
AppCompatActivity.MODE_PRIVATE
)
when (sharedPreferences.getInt(CatalystConstants.prefName, CatalystConstants.themeLight)) {
CatalystConstants.themeLight -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}
CatalystConstants.themeDark -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
}
}
}```
If there are only two possible values, you can do this, although in my opinion when would be much clearer to read because the comparison is so long.
appCompatDelegate.defaultNightMode = if(sharedPreferences.getInt(CatalystConstants.prefName, CatalystConstants.themeLight)
== CatalystConstants.themeLight)
AppCompatDelegate.MODE_NIGHT_NO
else // themeDark
AppCompatDelegate.MODE_NIGHT_YES
With when:
appCompatDelegate.defaultNightMode =
when (sharedPreferences.getInt(CatalystConstants.prefName, CatalystConstants.themeLight)) {
CatalystConstants.themeLight -> AppCompatDelegate.MODE_NIGHT_NO
CatalystConstants.themeDark -> AppCompatDelegate.MODE_NIGHT_YES
}
Could be more concise using run. It's not very clear, but that's because you aren't using all caps for your constant names:
CatalystConstants.run {
appCompatDelegate.defaultNightMode =
when (sharedPreferences.getInt(prefName, themeLight)) {
themeLight -> AppCompatDelegate.MODE_NIGHT_NO
themeDark -> AppCompatDelegate.MODE_NIGHT_YES
}
}
There is not if-else ternary expresion in Kotlin but you can write if-else statment in this way:
if (a > b) a else b
It's the most similar way to ternary if-else
For simple check like
if (variable != null) {
doSomething(variable)
}
We could change to
variable?.let { doSometing(it) }
However for a case with else
if (variable != null) {
doSomething(variable)
} else {
doOtherThing()
}
Is there a way of doing so in a single function? Is there something like either?
You can use the elvis-operator ?: like so:
variable?.let { doSomething(it) } ?: doOtherThing()
However personally I think that this gets quite unreadable very quickly. There is nothing wrong with an if-expression.
Another approach which might ring well with functional style is the use of when, which is very close to pattern matching:
when(variable) {
null -> doOtherThingSomething()
else -> doSomething(variable)
}
I would argue, however, that you should be careful with abandoning the classic if statement, if what you're after is its natural purpose - conditional execution. If you're calculating a value in two different ways and then using it in the same way, this approach makes sense. But if your code paths diverge at this point and don't meet again, then if provides a clearer intent.
You can map null-able value if not null by using ternary operator to check not null condition with If...Else Statements.
Here, I had wrote some code snippet to check value null or not ,
Case 1: value initialized
fun main(args: Array<String>) {
val value:Int ?= 10
val mapped = value?.let { "Value is == $value" } ?: "Value not initialized..."
println(mapped)
}
You gotta result: Value is == 10
Case 2: value set remains null
fun main(args: Array<String>) {
val value:Int ?= null
val mapped = value?.let { "Value is == $value" } ?: "Value not initialized..."
println(mapped)
}
You gotta result: Value not initialized...