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}")
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 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]
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.
In Python, there are list comprehensions and similar constructs for maps and sets. In Kotlin there is nothing at all in any of the documentation with a similar name.
What are the equivalents of these comprehensions? For example, those found in Python 3 Patterns, Recipes and Idioms. Which includes comprehensions for:
list
set
dictionary
Note: this question is intentionally written and answered by the author (Self-Answered Questions), so that the idiomatic answers to commonly asked Kotlin topics are present in SO.
Taking examples from Python 3 Patterns, Recipes and Idioms we can convert each one to Kotlin using a simple pattern. The Python version of a list comprehension has 3 parts:
output expression
input list/sequence and variable
optional predicate
These directly correlate to Kotlin functional extensions to collection classes. The input sequence, followed by the optional predicate in a filter lambda, followed by the output expression in a map lambda. So for this Python example:
# === PYTHON
a_list = [1, 2, 3, 4, 5, 6]
# output | var | input | filter/predicate
even_ints_squared = [ e*e for e in a_list if e % 2 == 0 ]
print(even_ints_squared)
# output: [ 4, 16, 36 ]
Becomes
// === KOTLIN
var aList = listOf(1, 2, 3, 4, 5, 6)
// input | filter | output
val evenIntsSquared = aList.filter { it % 2 == 0 }.map { it * it }
println(evenIntsSquared)
// output: [ 4, 16, 36 ]
Notice that the variable is not needed in the Kotlin version since the implied it variable is used within each lambda. In Python you can turn these into a lazy generator by using the () instead of square brackets:
# === PYTHON
even_ints_squared = ( e**2 for e in a_list if e % 2 == 0 )
And in Kotlin it is more obviously converted to a lazy sequence by changing the input via a function call asSequence():
// === KOTLIN
val evenIntsSquared = aList.asSequence().filter { it % 2 == 0 }.map { it * it }
Nested comprehensions in Kotlin are created by just nesting one within the other's map lambda. For example, take this sample from PythonCourse.eu in Python changed slightly to use both a set and a list comprehension:
# === PYTHON
noprimes = {j for i in range(2, 8) for j in range(i*2, 100, i)}
primes = [x for x in range(2, 100) if x not in noprimes]
print(primes)
# output: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
Becomes:
// === KOTLIN
val nonprimes = (2..7).flatMap { (it*2..99).step(it).toList() }.toSet()
val primes = (2..99).filterNot { it in nonprimes }
print(primes)
// output: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
Notice that the nested comprehension produces a list of lists which is converted to a flat list using flatMap() and then converted to a set using toSet(). Also, Kotlin ranges are inclusive, whereas a Python range is exclusive so you will see the numbers are slightly different in the ranges.
You can also use a sequence generator with co-routines in Kotlin to yield the values without needing the call to flatMap() or flatten():
// === KOTLIN
val nonprimes = sequence {
(2..7).forEach { (it*2..99).step(it).forEach { value -> yield(value) } }
}.toSet()
val primes = (2..99).filterNot { it in nonprimes }
Another example from the referenced Python page is generating a matrix:
# === PYTHON
matrix = [ [ 1 if item_idx == row_idx else 0 for item_idx in range(0, 3) ] for row_idx in range(0, 3) ]
print(matrix)
# [[1, 0, 0],
# [0, 1, 0],
# [0, 0, 1]]
And in Kotlin:
// === KOTLIN
val matrix = (0..2).map { row -> (0..2).map { col -> if (col == row) 1 else 0 }}
println(matrix)
// [[1, 0, 0],
// [0, 1, 0],
// [0, 0, 1]]
Or in Kotlin instead of lists, you could also generate arrays:
// === KOTLIN
val matrix2 = Array(3) { row ->
IntArray(3) { col -> if (col == row) 1 else 0 }
}
Another of the examples for set comprehensions is to generate a unique set of properly cased names:
# === PYTHON
names = [ 'Bob', 'JOHN', 'alice', 'bob', 'ALICE', 'J', 'Bob' ]
fixedNames = { name[0].upper() + name[1:].lower() for name in names if len(name) > 1 }
print(fixedNames)
# output: {'Bob', 'Alice', 'John'}
Is translated to Kotlin:
// === KOTLIN
val names = listOf( "Bob", "JOHN", "alice", "bob", "ALICE", "J", "Bob" )
val fixedNames = names.filter { it.length > 1 }
.map { it.take(1).toUpperCase() + it.drop(1).toLowerCase() }
.toSet()
println(fixedNames)
// output: [Bob, John, Alice]
And the example for map comprehension is a bit odd, but can also be implemented in Kotlin. The original:
# === PYTHON
mcase = {'a':10, 'b': 34, 'A': 7, 'Z':3}
mcase_frequency = { k.lower() : mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys() }
print(mcase_frequency)
# output: {'a': 17, 'z': 3, 'b': 34}
And the converted, which is written to be a bit more "wordy" here to make it clearer what is happening:
// === KOTLIN
val mcase = mapOf("a" to 10, "b" to 34, "A" to 7, "Z" to 3)
val mcaseFrequency = mcase.map { (key, _) ->
val newKey = key.toLowerCase()
val newValue = mcase.getOrDefault(key.toLowerCase(), 0) +
mcase.getOrDefault(key.toUpperCase(), 0)
newKey to newValue
}.toMap()
print(mcaseFrequency)
// output: {a=17, b=34, z=3}
Further reading:
Kotlin adds more power than list/set/map comprehensions because of its extensive functional transforms that you can make to these collection types. See What Java 8 Stream.collect equivalents are available in the standard Kotlin library?
for more examples.
See Get Factors of Numbers in Kotlin
which shows another example of a Python comprehension versus Kotlin.
See Kotlin Extensions Functions for Collections in the API reference guide.
Just for exercise the closest to python will be:
infix fun <I, O> ((I) -> O).`in`(range: Iterable<I>): List<O> = range.map(this).toList()
infix fun <I> Iterable<I>.`if`(cond: (I) -> Boolean): List<I> = this.filter(cond)
fun main() {
{ it: Int -> it + 1 } `in` 1..2 `if` {it > 0}
}
val newls = (1..100).filter({it % 7 == 0})
in Kotlin is equivalent to the following Python code
newls = [i for i in 0..100 if i % 7 ==0]
Map comprehension
import kotlin.math.sqrt
val numbers = "1,2,3,4".split(",")
val roots = numbers.associate { n -> n.toInt() to sqrt(n.toFloat()) }
println(roots) // prints {1=1.0, 2=1.4142135, 3=1.7320508, 4=2.0}
If keys are untransformed elements of source list, even simpler:
val roots = numbers.associateWith { n -> sqrt(n.toFloat()) }
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