I noticed that Kotlin has two built in methods reversed() and asReversed()
Is there any difference between these two? or are they essentially just doing the exact same thing?
In Kotlin, both reversed and asReversed have their own unique functions.
The Reverse function returns a list with elements in reversed: order.
Reversed Function
Whereas, the asReversed function returns a reversed read-only view of the original List i.e., all changes made in the original list will be reflected in the reversed one.
asReversed Function
The difference between the two are that once the asReversed() function has been used, any changes in the original list will be reflected in the reversed list as well.
But the same doesn't hold valid or true when the reversed() function is being used. It's merely used to reverse a list.
Example:
val list = mutableListOf(0, 1, 2, 3, 4, 5)
val asReversed = list.asReversed()
val reversed = list.reversed()
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
list[0] = 10
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
Outputs
Original list: [0, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 0]
reversed: [5, 4, 3, 2, 1, 0]
Original list: [10, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 10]
reversed: [5, 4, 3, 2, 1, 0]
Try it online!
As per the document
1. reversed()
It only returns a list (List<>) with elements in reversed order.
There are multiple definitions of this extension on different Objects like Array, List, etc.
Example of extension on
Array<>
/**
* Returns a list with elements in reversed order.
*/
public fun <T> Array<out T>.reversed(): List<T> {
if (isEmpty()) return emptyList()
val list = toMutableList()
list.reverse()
return list
}
List<>
/**
* Returns a list with elements in reversed order.
*/
public fun <T> Iterable<T>.reversed(): List<T> {
if (this is Collection && size <= 1) return toList()
val list = toMutableList()
list.reverse()
return list
}
2. asReversed()
It is only applicable to List<> and returns a reversed read-only view of the original List. All changes made in the original list will be reflected in the reversed one.
Related
Pretty much the title. I want to map through a list to create a new list, but the logic for transforming each element depends on previous values that have been already transformed.
For a simple example, I have a list val myList = [1, 2, 3, 4, 5] and I want to map through each value, where each new value is the sum of the current element plus the previous transformed element. Meaning, I want the result to be [1, 3, 6, 10, 15]. This is not a real scenario, just for sake of example.
I can map through my list but I don't know how to reference the new list that's currently being built:
myList.map { it + list_that's_currently_being_built[i-1] }
runningReduce
fun main() {
val myList = listOf(1, 2, 3, 4, 5)
val result = myList.runningReduce { acc, value -> acc + value }
println(result) // [1, 3, 6, 10, 15]
}
I'm trying to filter certain items out of a list and merge them in the final list in a specific order. The first code snippet seems inefficient since it creates 2 lists for filtering & then iterates over them however that code works. The second snippet is trying to combine both filterings however the map operator is not adding items to otherNums list
Could someone please help me understand why is this happening?
Snippet 1:
fun main() {
val favItem = 0
val list = listOf(11, 12, 13, 2,3,4,5,6,7,10, favItem)
val greaterThan10 = list.filter{item -> item > 10}
val otherNums = list.asSequence().filter{item -> item != favItem}.filter{item -> item < 10}
println(" $greaterThan10") //the list is filled with proper numbers
println("merged list ${greaterThan10.plus(favItem).plus(otherNums)}")
}
Result:
[11, 12, 13]
merged list [11, 12, 13, 0, 2, 3, 4, 5, 6, 7]
Snippet 2:
fun main() {
val favItem = 0
val list = listOf(11, 12, 13, 2,3,4,5,6,7,10, favItem)
val greaterThan10 = mutableListOf<Int>()
val otherNums = list.asSequence().filter{item -> item != favItem}.map{
if(it > 10) {
greaterThan10.add(it)
}
it
}
.filter{item -> item != 10}
println("$greaterThan10") // the list is empty
println("merged list ${greaterThan10.plus(favItem).plus(otherNums)}")
}
Result:
[]
merged list [0, 11, 12, 13, 2, 3, 4, 5, 6, 7]
In your second snippet, greaterThan10 list is empty because of the lazy behavior of sequences, a sequence is iterated only when a terminal operation is encountered like toList() or sum().
In your case, the sequence is iterated when you write .plus(otherNums). List + Sequence produces a List. If you print your greaterThan10 list after printing the merged list, you will find it populated.
Btw, you don't need a Sequence here. Two major cases where sequences are more performant than lists are:
When you have lot of intermediate operations, like map, filter etc.
With Iterable a lot of intermediate Iterables are created which consume more memory,
When you have some kind of a short-circuit operation at the end like take(), contains(), first() etc. i.e. when the entire collection needs not be iterated to get the final result.
As per the docs,
The lazy nature of sequences adds some overhead which may be significant when processing smaller collections or doing simpler computations. Hence, you should consider both Sequence and Iterable and decide which one is better for your case.
For the final solution, I think you can use your snippet 1. That looks good to me, just remove the unnecessary asSequence and combine the two filter into one.
fun main() {
val favItem = 0
val list = listOf(11, 12, 13, 2, 3, 4, 5, 6, 7, 10, favItem)
val greaterThan10 = list.filter {item -> item > 10}
val otherNums = list.filter { it != favItem && it <= 10 }
println(" $greaterThan10")
println("merged list ${greaterThan10 + favItem + otherNums}")
}
I think using filter is better than minus on lists as the latter has quadratic worst case time complexity (if I remember correctly).
I wrote a small example to demonstrate the difference. Run this a few times to see the difference.
Also, as #IvoBeckers mentioned in the comment,
"If the original list doesn't have a favItem this method will also add one to it. And if the list has multiple of the favItem this method will replace it with a single one."
val favItem = 0
val list = listOf(11, 12, 13, 2, 3, 4, 5, 6, 7, 10, favItem)
val greaterThan10 = list.filter { it > 10 }
val otherNums = list - greaterThan10.toSet() - favItem
println("greaterThan10: $greaterThan10") // [11, 12, 13]
println("otherNums: $otherNums") // [2, 3, 4, 5, 6, 7, 10]
println("merged list: ${greaterThan10 + favItem + otherNums}")
Edit: replaced .minus(...) with -. Thanks to #Ivo Beckers' comment.
val favItem = 0
val list = listOf(11, 12, 13, 2, 3, 4, 5, 6, 7, 10, favItem)
val (greaterThan10, otherNums) = list // destructuring assignment
.filter { it != favItem } // filter out favItem
.groupBy { it > 10 } // create two groups
.toSortedMap { o1, _ -> if (o1) -1 else 1 } // sort groups in the order [true, false]
.map { it.value.toList() } // return the two lists
println("greaterThan10: $greaterThan10")
println("otherNums: $otherNums")
println("merged list: ${greaterThan10 + favItem + otherNums}")
As stated in the answer https://stackoverflow.com/a/57115894/3286489, the asReversed() will generate a reversed list, that value will be changed if the original list has changed its element within.
val list = mutableListOf(0, 1, 2, 3, 4, 5)
val asReversed = list.asReversed()
val reversed = list.reversed()
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
list[0] = 10
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
Outputs
Original list: [0, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 0]
reversed: [5, 4, 3, 2, 1, 0]
Original list: [10, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 10]
reversed: [5, 4, 3, 2, 1, 0]
To me, that means only if the original list is a MutableList then it can change it's value within.
However, if the original List is an immutable List, it's value cannot be changed, this essentially make asReversed() and reversed() has not distinct difference on it, right?
i.e.
val list = listOf(0, 1, 2, 3, 4, 5)
val asReversed = list.asReversed() // This is the same as below?
val reversed = list.reversed() // This is the same as above?
Did I miss any scenario they are still different?
Updated
I even change what it contains as mutableList
val list = listOf(
mutableListOf(1),
mutableListOf(2),
mutableListOf(3),
mutableListOf(4),
mutableListOf(5),
mutableListOf(6))
val asReversed = list.asReversed()
val reversed = list.reversed()
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
list[0][0] = 10
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
Output
Original list: [[1], [2], [3], [4], [5], [6]]
asReversed: [[6], [5], [4], [3], [2], [1]]
reversed: [[6], [5], [4], [3], [2], [1]]
Original list: [[5], [2], [3], [4], [5], [6]]
asReversed: [[6], [5], [4], [3], [2], [10]]
reversed: [[6], [5], [4], [3], [2], [10]]
This will change for both asReversed and reversed result
The most important difference between the two is, that asReversed is always just a view on the original list. That means, if you alter the original list, the result of asReversed will always contain all the updated information. In the end it's just a view.
reversed however always gives you a new disconnected list of the original list. Note however that when you deal with object references within the original list (like lists in a list or other kind of objects that are referenceable) then you will see all the adaptations of the original object of that list also adapted in the reversed ones, regardless of whether you used reversed or asReversed.
Regarding your update it is unfortunate you used such a list that contains references. With your original list however the difference becomes much clearer:
val asReversed = list.asReversed()
val reversed = list.reversed()
fun printAll(msg : String) {
println(msg)
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
}
printAll("Initial ----")
list as MutableList
list[2] = 1000
printAll("mutating original list ----")
reversed as MutableList
reversed[4] = 3000
printAll("mutating reversed list ----")
As asReversed returns you a ReversedListReadOnly-type, you can't easily cast it to a MutableList, but if it would be possible, the changes in asReversed would be reflected in the original list, but not in the reversed. The output of the above is the following:
Initial ----
Original list: [0, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 0]
reversed: [5, 4, 3, 2, 1, 0]
mutating original list ----
Original list: [0, 1, 1000, 3, 4, 5]
asReversed: [5, 4, 3, 1000, 1, 0]
reversed: [5, 4, 3, 2, 1, 0]
mutating reversed list ----
Original list: [0, 1, 1000, 3, 4, 5]
asReversed: [5, 4, 3, 1000, 1, 0]
reversed: [5, 4, 3, 2, 3000, 0]
As you can see: the changes are nicely reflected in the original and the asReversed-list, but changes in the reversed are not reflected and also the reversed one will not contain the original list adaptations.
So, yes, you probably missed that scenario... the lists (reversed and asReversed) aren't equal, not even, if you have a readonly view as input, as noone can guarantee you that the list isn't altered... neither the original nor the asReversed one.
To add to other answers, this is an example of a general method-naming convention in Kotlin (and, to an extent in Java):
toNoun() methods convert an object into a new form that's independent of the original.
asNoun() methods return a view onto the existing object; changes in that will be reflected in the view and (if appropriate) vice versa.
verb() methods mutate the object directly (and don't return anything).
verbed() methods return a mutated copy of the object, leaving the original unchanged.
This question provides examples of two of those cases.
These conventions are very lightweight, read well, and are used fairly consistently. After you've seen a few, you know almost instinctively how a method will behave!
(It's not universal, though; for example, map() is a verb, but doesn't mutate its receiver. However, methods like map() and filter() long pre-date Kotlin, and so it's arguably better to stick to well-known existing names like that.)
Their difference actually amazed me!
Okay so by browsing the std-lib this is what I've found.
The reversed function actually creates a copy of whatever the Iterable is as a MutableList and reverses the list "really" and then returns it.
public fun <T> Iterable<T>.reversed(): List<T> {
if (this is Collection && size <= 1) return toList()
val list = toMutableList()
list.reverse()
return list
}
While when you call the asReversed(), it doesn't create a new list "by copying the elements".
It just creates an implementation of an Abstract list with delegation to the real list, and overriding the getter.
public fun <T> List<T>.asReversed(): List<T> = ReversedListReadOnly(this)
private open class ReversedListReadOnly<out T>(private val delegate: List<T>) : AbstractList<T>() {
override val size: Int get() = delegate.size
override fun get(index: Int): T = delegate[reverseElementIndex(index)]
}
So there's no overhead, since the list is immutable there is no need to touch any other part, neither need to copy the list i.e. no need for creating a new one and allocate its memory. It is simplified and just uses the original list.
Found this example here https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/as-reversed.html
val original = mutableListOf('a', 'b', 'c', 'd', 'e')
val originalReadOnly = original as List<Char>
val reversedA = originalReadOnly.asReversed()
val reversedB = originalReadOnly.reversed()
println(original) // [a, b, c, d, e]
println(reversedA) // [e, d, c, b, a]
println(reversedB)
// changing the original list affects its reversed view
original.add('f')
println(original) // [a, b, c, d, e, f]
println(reversedA) // [f, e, d, c, b, a]
println(reversedB) // [e, d, c, b, a]
original[original.lastIndex] = 'z'
println(original) // [a, b, c, d, e, z]
println(reversedA) // [z, e, d, c, b, a]
println(reversedB) // [e, d, c, b, a]
It does show that a List can still be changed if it was originally from a MutableList, where we change the MutableList. Hence that makes asReversed() is still different from reversed() in this case, since there's a way to change the List by changing the original MutableList
I came across the following Kotlin code:
single(name = walletOkHttpTag) {
createOkHttpClient {
addHeaders(
*mutableListOf<Pair<String, String>>().apply {
add(HeaderKey.ACCEPT to APPLICATION_JSON_HEADER)
if (isDebug || isBeta) {
add(HeaderKey.AUTHORIZATION to BASIC_AUTH_WALLET_STAGE_HEADER)
}
}.toTypedArray()
)
}
}
What does the asterisk * mean that is in front of mutableListOf?
This is the spread operator and it is required to pass an existing array to a vararg function.
When we call a vararg-function, we can pass arguments one-by-one, e.g. asList(1, 2, 3), or, if we already have an array and want to pass its contents to the function, we use the spread operator (prefix the array with *):
Simplified example from the documentation:
val a = arrayOf(1, 2, 3)
val list = listOf(-1, 0, *a, 4)
println(list)
Output:
[-1, 0, 1, 2, 3, 4]
Without the spread operator, the array itself would be added as a single element, resulting in a List<Serializable> with 4 elements:
[-1, 0, [Ljava.lang.Integer;#31befd9f, 4]
Is there a simple way to divide list into parts (maybe some lambda) in Kotlin?
For example:
[1, 2, 3, 4, 5, 6] => [[1, 2], [3, 4], [5, 6]]
Since Kotlin 1.2 you can use Iterable<T>.chunked(size: Int): List<List<T>> function from stdlib (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/chunked.html).
Given the list: val list = listOf(1, 2, 3, 4, 5, 6) you can use groupBy:
list.groupBy { (it + 1) / 2 }.map { it.value }
Or if your values are not numbers you can first assign an index to them:
list.withIndex()
.groupBy { it.index / 2 }
.map { it.value.map { it.value } }
Or if you'd like to save some allocations you can go a bit more manual way with foldIndexed:
list.foldIndexed(ArrayList<ArrayList<Int>>(list.size / 2)) { index, acc, item ->
if (index % 2 == 0) {
acc.add(ArrayList(2))
}
acc.last().add(item)
acc
}
The better answer is actually the one authored by VasyaFromRussia.
If you use groupBy, you will have to add and index and then post-process extracting the value from an IndexedValue object.
If you use chunked, you simply need to write:
val list = listOf(10, 2, 3, 4, 5, 6)
val chunked = list.chunked(2)
println(chunked)
This prints out:
[[10, 2], [3, 4], [5, 6]]
Nice way of dividing list is by the use of function partition. Unlike groupBy it doesn't divide list by keys but rather by predicate which gives out Pair<List, List> as a result.
Here's an example:
val (favorited, rest) = posts.partition { post ->
post.isFavorited()
}
favoritedList.addAll(favorited)
postsList.addAll(rest)
The API says there is a GroupBy function, which should do what you want.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/group-by.html
Or use sublist and break it up yourself
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/sub-list.html
If you want to divide a list into N parts.
(and not divide a list into parts of size N)
You can still use the chunked answer:
https://stackoverflow.com/a/48400664/413127
Only, first you need to find your chunk size.
val parts = 2
val list = listOf(10, 2, 3, 4, 5, 6)
val remainder = list.size % 2 // 1 or 0
val chunkSize = (list.size / parts) + remainder
val chunked = list.chunked(chunkSize)
println(chunked)
This prints out
[[10, 2, 3], [4, 5, 6]]
or when
val parts = 3
This prints out
[[10, 2], [3, 4], [5, 6]]
Interesting answer in Python here: Splitting a list into N parts of approximately equal length