0

I need to perform some operations on a mutable list and use its previous value. Something similar to also except that it should return the previous value.

val localList: List<String> = mainList.alsoButGetPrevious { it.clear() }

Before I write this function, is there an existing one that does this?

k314159
  • 5,051
  • 10
  • 32

3 Answers3

3

There is no "previous value" in this case. There is only one list and it is modified by clear(), so even if you would do something like this:

localList = mainList
mainList.clear()

Then localList would still not contain "previous" value, but it would be empty.

What you need here is to copy the list before clearing it. There is no scoping function for this, because it is specific to lists only. You can implement it by yourself:

val localList = mainList.copyAndApply { clear() }

inline fun <T> MutableList<T>.copyAndApply(block: MutableList<T>.() -> Unit): List<T> {
    val result = toList()
    block()
    return result
}

As noted by @mightyWOZ, toList() creates a shallow copy of the data, meaning that it contains references to the same object instances as the original list. Above solution works if you need to only clear mainList or add/remove items from it. However, if you modify objects in mainList then this change will affect localList as well. There is no straightforward and universal way to perform a deep copy of the data in Java/Kotlin. If you need this, it is probably better to create utility functions that target your data structures.

broot
  • 21,588
  • 3
  • 30
  • 35
  • Please note that `toList` would create a [shallow copy](https://stackoverflow.com/questions/1175620/in-java-what-is-a-shallow-copy) – mightyWOZ Sep 17 '21 at 09:56
3

As broot's answer says, there is only one list, and you should copy it with toList. You can do this inline with scope functions like this:

val localList: List<String> = mainList.run { 
    // here, this: MutableList<T>
    toList().also { clear() } // here we are calling this.clear()
}

or if you don't mind repeating mainList,

val localList: List<String> = mainList.toList().also {
    mainList.clear()
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Am I missing something or this could just be `mainList.toList().also { mainList.clear() }` ? – Joffrey Sep 17 '21 at 09:37
  • Oh wow, this is both clever and confusing as hell :-D – broot Sep 17 '21 at 09:37
  • @Joffrey Yes, but I don't like repeating `mainList`. Isn't that the whole point of scope functions? What if `mainList` was another long expression? – Sweeper Sep 17 '21 at 09:38
  • It's not a long expression though, and if it were I would clearly urge the writer to extract a variable anyway :D Honestly I find the repetition way more readable here than playing with `this` in a `run`+`also` combo. It's almost shorter to repeat the variable. The point is to make it easy to understand for the reader – Joffrey Sep 17 '21 at 09:42
1

You can use Delagetes.observable or vetoable to observe or even with vetoable set under which conditions it should change

var myList: List<String> by Delegates.observable(listOf()) { 
        property: KProperty<*>, oldValue: List<String>, newValue: List<String> ->

}

var myList: List<String> by Delegates.vetoable(listOf()) { property: KProperty<*>, oldValue: List<String>, newValue: List<String> ->
    newValue.size > 5
}
Thracian
  • 43,021
  • 16
  • 133
  • 222