I have this expression:
var previousState = state.previousState
while (previousState != null && previousState::class != searchClass) {
previousState = previousState.previousState
}
Can I do it without while and var in kotlin way?
I believe something like this will work out:
val seq = generateSequence(state.previousState) { previousState ->
previousState.previousState
}
val result = seq.firstOrNull { it::class == searchClass }
What is kotlin way?
Make your algorithm a recursive function, and it will work in any language without while:
private tailrec fun previousState(state: State?) : State? {
if(state != null && state::class != searchClass) {
return previousState(state.previousState)
}
return state
}
Related
Every so often, I find myself wanting to compute a value for some sort of filter operation, but then wanting to use that value when it's already disappeared into the condition-checking thing.
For instance:
val found = list.firstOrNull { slowConversion(it).isWanted() }
if (found != null) {
something(found, slowConversion(found))
}
or
when {
other_conditions -> other_actions
list.any { it.contains(regex1) } -> something(list.firstOrNull { it.contains(regex1) } ?: "!!??")
}
For the slowConversion() I can work with a sequence mapped to pairs, although the terms first and second kinda confuse things a bit...
val pair = list.asSequence().map { it to slowConversion(it) }.firstOrNull { it.second.isWanted() }
if ( pair != null ) {
something(pair.first, pair.second)
}
or if I only want the conversion,
val converted = list.firstNotNullOfOrNull { slowConversion(it).takeIf { it.isWanted() } }
but the best I can come up with to avoid the when duplication involves moving the action part into the condition part!
fun case(s: List<String>, r: Regex) {
val match = s.firstOrNull { it.contains(r) }?.also { something(it) }
return match != null
}
when {
other_conditions -> other_actions
case(list, regex1) -> true
}
At this point, it seems I should just have a stack of function calls linked together with ||
other_things || case(list, regex1) || case(list, regex2) || catchAll(list)
Is there something better or more concise for either of these?
You can write your first example like this:
for(element in list) {
val result = slowConversion(element)
if(result.isWanted()) {
something(element, result)
break
}
}
This might not look very Kotlin-ish, but I think it's pretty straightforward & easy to understand.
For your second example, you can use the find function:
when {
other_conditions -> other_actions
else -> list.find { it.contains(regex1) }?.let(::something)
}
If you have multiple regexes, just iterate over them,
val regexes = listOf(regex1, regex2, ...)
for(regex in regexes) {
val element = list.find { it.contains(regex1) } ?: continue
something(element)
break
}
I have a function which has quite a lot lines. In that function I have a .filter{} like:
fun getMyListForFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag == Query.IS_FOO }
.map{
//..mappings
}
}
and then I have a second function just to retrieve queries which are NOT Foo:
fun getMyListForNotFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag != Query.IS_FOO }
.map{
//..mappings
}
}
As you can the only difference is the == or != operator in the .filter function. Although I have all the previous lines duplicated..
I bet there is a nice Kotlin way to enhance this code?
Pass a predicate as a parameter to your function for filtering the list.
fun getMyList(predicate: (YourType) -> Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter(predicate)
.map{
//..mappings
}
}
Usage:
val listForFoo = getMyList { it.flag == Query.IS_FOO }
val listForNotFoo = getMyList { it.flag != Query.IS_FOO }
OR, if you just want to pass a Boolean, you can also do that:
fun getMyList(filterFoo: Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter {
val isFoo = it.flag == Query.IS_FOO
if(filterFoo) isFoo else !isFoo
}
.map{
//..mappings
}
}
I would use partition directly.
I created a sample in kotlinlang.org's playground and it looks like this:
// Given a "thing"
data class Thing(val id: Int, val isFoo: Boolean)
// Have a function that simplifies this:
fun filterThings(source: List<Thing>) = source.partition { it.isFoo }
// Alternatively, you could have a more generic one:
fun filterThings(source: List<Thing>,
predicate: ((Thing) -> Boolean)) = source.partition(predicate)
// And you can use either like so:
// Given the source
val source = listOf(Thing(1, true),
Thing(2, true),
Thing(3, false),
Thing(4, true),
Thing(5, false),
Thing(6, false))
// Filter them with the non-configurable version:
val results = filterThings(source)
// or the more configurable one where *you* supply the predicate:
val results = filterThings(source) { it.isFoo }
The results are going to be:
results.first is going to be the one that pass the predicate, and the rest will be in results.second:
results.first = [Thing(id=1, isFoo=true), Thing(id=2, isFoo=true), Thing(id=4, isFoo=true)]
results.second = [Thing(id=3, isFoo=false), Thing(id=5, isFoo=false), Thing(id=6, isFoo=false)]
If kotlin promotes safe code such as:
val currentName = "Some Guy"
getDataFromServer()?.getUsers()?.find { it.name == currentName }?.profilePicture?.let {
showPicture(it)
} ?: let {
showAddPictureButton()
}
Why there is no similar syntax for exception handling, for example using imaginary ??. operator:
val someUserId = 123
val newName = "Cool Dude"
connectToDatabase()??.getDao<UserDao>()??.changeUserName(someUserId, newName)??.let { newUserData ->
reloadView(newUserData)
} ??: let { error ->
interpretAndHandleError(error)
}
I don't see drawbacks here. Is there any reason this is not a part of language?
It isn't about drawbacks, but about not having sufficient benefits. Consider writing your first example without ?.:
val data = getDataFromServer()
if (data != null) {
val users = data.getUsers()
if (users != null) {
val user = users.find { it.name == currentName }
if (user != null) ...
} else null
} else null
You can see it's much more complex than the original code. Now consider writing your suggested ??. without it:
try {
connectToDatabase().getDao<UserDao>().changeUserName(someUserId, newName).let { newUserData ->
reloadView(newUserData)
}
} catch(error: Exception) {
interpretAndHandleError(error)
}
Where is the complexity you want to eliminate?
The following code can work well, but the code of fun addDetail(...) is too complex, is there a simple way to do that ? Thanks!
BTW, in the fun addDetail(...), aMListDetail maybe null, and aMListDetail?.innerListDetail maybe null.
data class MDetail (
val _id: Long
)
class DetailsHandler(mContext: Context = UIApp.instance) {
data class MListDetail(val innerListDetail: MutableList<MDetail>)
private var aMListDetail: MListDetail?
var mJson: String by PreferenceTool(mContext,"mySavedJson", "")
init {
aMListDetail= Gson().fromJson(mJson,MListDetail::class.java)
}
fun addDetail(aMDetail:MDetail){
if (aMListDetail==null){
aMListDetail=MListDetail(mutableListOf(aMDetail))
}else{
if (aMListDetail?.innerListDetail==null){
aMListDetail=MListDetail(mutableListOf(aMDetail))
}else {
aMListDetail?.innerListDetail?.add(aMDetail)
}
}
mJson = Gson().toJson(aMListDetail)
}
}
fun addDetail(aMDetail: MDetail) {
if (aMListDetail?.innerListDetail == null) {
aMListDetail = MListDetail(mutableListOf(aMDetail))
} else {
aMListDetail.innerListDetail.add(aMDetail)
}
mJson = Gson().toJson(aMListDetail)
}
Alternative:
fun addDetail(aMDetail: MDetail) {
if (aMListDetail?.innerListDetail == null) {
aMListDetail = MListDetail(mutableListOf())
}
aMListDetail.innerListDetail.add(aMDetail)
mJson = Gson().toJson(aMListDetail)
}
You don't need null-safe ?. operators in your add() call, since at that point you've already checked that aMListDetail != null and innerListDetail != null.
BTW, in the fun addDetail(...), aMListDetail maybe null,
Why not fix the problem at the source? You initialize it in the constructor, then tell Kotlin it could be set to null, but actually you never do this!
If you remove the unused nullability, the code simplifies to:
class DetailsHandler(mContext: Context = UIApp.instance) {
data class MListDetail(val innerListDetail: MutableList<MDetail>)
var mJson: String by PreferenceTool(mContext,"mySavedJson", "")
// can even be val
private var aMListDetail: MListDetail
init {
aMListDetail= Gson().fromJson(mJson,MListDetail::class.java)
}
fun addDetail(aMDetail:MDetail){
aMListDetail.innerListDetail.add(aMDetail)
mJson = Gson().toJson(aMListDetail)
}
}
If your real code doesn't initialize it at the beginning, consider by lazy or by notNull.
and aMListDetail?.innerListDetail maybe null.
Only is aMListDetail is null, which you should avoid as above.
Finally, if you really need aMListDetail to be null sometimes, you can write
aMListDetail?.let {
it.innerListDetail.add(aMDetail)
}
(which does nothing if aMListDetail is null)
fun addDetail(aMDetail:MDetail){
if (aMListDetail?.innerListDetail==null){
aMListDetail=MListDetail(mutableListOf(aMDetail))
}else {
aMListDetail?.innerListDetail?.add(aMDetail)
}
mJson = Gson().toJson(aMListDetail)
}
8-)
I am looking for an idiomatic way to return if not null a variable in Kotlin. For example, I would like something such as:
for (item in list) {
getNullableValue(item).? let {
return it
}
}
But it's not possible to return inside a let block in Kotlin.
Is there a good way to do this without having to do this:
for (item in list) {
val nullableValue = getNullableValue(item)
if (nullableValue != null) {
return nullableValue
}
}
Not sure if this would be called idiomatic, but you could do this:
val nullableValue = list.find { it != null }
if (nullableValue != null) {
return nullableValue
}
Edit:
Based on s1m0nw1's answer, you can probably reduce it to this:
list.find { it != null }?.let {
return it
}
It is possible to return from let, as you can read in the documentation:
The return-expression returns from the nearest enclosing function, i.e. foo. (Note that such non-local returns are supported only for lambda expressions passed to inline functions.)
let() is an inline function and therefore you automatically return from the enclosing function whenever you do return within let, like in this example:
fun foo() {
ints.forEach {
if (it == 0) return // nonlocal return from inside lambda directly to the caller of foo()
print(it)
}
}
To modify the behavior, "labels" can be used:
fun foo() {
ints.forEach lit# {
if (it == 0) return#lit
print(it)
}
}
The "right" idiomatic way of doing this is using the "first" method.
Example:
val x = listOf<Int?>(null, null, 3, null, 8).first { it != null }
His specific example would be
return list.first {getNullableValue(it) != null}
It could be something like:
for (item in list) {
getNullableValue(item)?.also {
return it
}
}
I am assuming the external loop is needed. If that is not the case, Ryba suggested solution should work.