Given a mutable list of: ```data class Foo(val id:...
# announcements
d
Given a mutable list of:
Copy code
data class Foo(val id: Int, val name: String)
What's an ideomatic way of searching for an
id
and replacing in-place the entry with another name (without turning the list into a map)? And return the id or the found entry as a bonus..
b
You can't replace the name as it's immutable. Or do you mean replacing entire element with a modified copy if it?
d
yes
a
β€’ do you want to replace it in-place or is it ok to remove the item and add the new one to the end of the list? β€’ do you want to replace only first or all occurrences?
d
Only the first, and better in-place.
b
Seems like some kind of pop operator would help here then. Or if you're happy with replacing all occurences, you can use map operator
d
map? That would just recreate the whole list, whereas now I have a mutable one...
a
And return the id or the found entry as a bonus
did you mean the old
name
maybe? why return
id
if it's going to be the same as the passed argument or null (if not found)?
d
In my case, I search on another field in the data class... I guess the contrived example is a bit too contrived πŸ˜…
a
ok, I read "or found entry" as "of found entry", but returning the entry makes sense
d
-- found entry is also fine... I could get the id from there I need this to create db repository stubs for unit testing... I thought it would be easier to simulate INSERT ... ON CONFLICT ... RETURNING id ... it just turned out not being such a one liner anymore...
a
I came up with this:
Copy code
fun MutableList<Foo>.replace(id: Int, name: String): Foo? {
    val index = indexOfFirst { it.id == id }
    return if (index > -1) {
        set(index, get(index).copy(name = name))
    } else {
        null
    }
}
βž• 1
d
Thanks! If I turn that into a more generic function, it should come in handy πŸ™‚, I wonder if this (in some form) could be an interesting addition to the stdlib? There's already a replaceAll...
a
Maybe πŸ€”
Copy code
fun <T> MutableList<T>.replace(predicate: (T) -> Boolean, transformation: (T) -> T): T? {
    val index = indexOfFirst(predicate)
    return if (index > -1) {
        set(index, transformation(get(index)))
    } else {
        null
    }
}

fun MutableList<Foo>.replace(id: Int, name: String): Foo? {
    return replace({ it.id == id }) { it.copy(name = name) }
}
πŸ‘ 1
πŸ‘πŸΌ 1
b
I'd also make it inline
πŸ‘ 1
πŸ‘πŸΌ 1
d
I did the same, but I used a receiver for the predicate... but I think I like yours better... since I think all the stdlib functions do that. Inline to save all the lambdas, I suppose. That's also an idea!
b
Even better yet, crossinline lambda args too
a
Even better yet, crossinline lambda args too
does that change anything in the case above?
b
It'll not allocate lambda args and instead just unfurl them in call sites, so you end up with code that only uses stdlib
Basically erasing the existence of your helper fun in bytecode alltogether
a
isn't it what
inline
does? bytecode seems to be exactly the same with and without
crossinline
e
don't use crossinline unless you need it, it prevents nonlocal returns
you can consider using https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/list-iterator.html to mutate a list entry in place instead of relying on indices
e.g.
βž• 1
a
I think the above needs some slight modification to return the value before transformation, rather than after transformation
e
I misread the previous code. behavior now matches
πŸ‘πŸΌ 1
πŸ‘ 1
d
Thanks! I also ended up making this one (also pretty handy for stubbing repositories in tests):
Copy code
fun <T> MutableList<T>.modify(predicate: (T) -> Boolean, transformation: T.() -> Unit): T? {
        val iterator = listIterator()
        while (iterator.hasNext()) {
            val value = iterator.next()
            if (predicate(value)) return value.also { value.transformation() }
        }
        return null
    }