I want to split a Vec into some parts of equal length, and then map over them. I have an iterator resulting from a call to Vec's chunks() method. This may leave me with a part that will be smaller than other parts, which will be the last element generated by it.
To be sure that all parts have equal length, I just want to drop that last element and then call map() on what's left.
As Sebastian Redl points out, checking the length of each chunk is the better solution for your specific case.
To answer the question you asked ("Use all but the last element from an iterator"), you can use Iterator::peekable to look ahead one. That will tell you if you are on the last item or not and you can decide to skip processing it if so.
let things = [0, 1, 2, 3, 4];
let mut chunks = things.chunks(2).peekable();
while let Some(chunk) = chunks.next() {
if chunks.peek().is_some() {
print!("Not the last: ");
} else {
print!("This is the last: ")
}
println!("{:?}", chunk);
}
To be sure that all parts have equal length, I just want to drop that last element
Always dropping the last element won't do this. For example, if you evenly chunk up your input, then always dropping the last element would lose a full chunk. You'd have to do some pre-calculation to decide if you need to drop it or not.
You can filter() the chunks iterator on the slice's len() being the amount you passed to chunks():
let things = [0, 1, 2, 3, 4];
for chunk in things.chunks(2).filter(|c| c.len() == 2) {
println!("{:?}", chunk);
}
This prints
[0, 1]
[2, 3]
As of Rust 1.31, you can use the chunks_exact method as well:
let things = [0, 1, 2, 3, 4];
for chunk in things.chunks_exact(2) {
println!("{:?}", chunk);
}
This prints
[0, 1]
[2, 3]
Note that the returned iterator also has the method remainder if you need to get the uneven amount of items at the very end.
If the collection being iterated over implements std::iter::DoubleEndedIterator, you can use the next_back() method for a solution that is (probably) marginally more performant than Shepmaster's solution and a little cleaner:
let things = [0, 1, 2, 3, 4];
let mut chunks = things.chunks(2);
let last = chunks.next_back().unwrap();
println!("Last: {:?}", last);
for chunk in chunks {
println!("Not last: {:?}", chunk);
}
next_back() eats the last element of the iterator, so after calling next_back() the iterator can be used to iterate over everything else. The output of the segment above:
Last: [4]
Not last: [0, 1]
Not last: [2, 3]
Since Rust 1.31.0, you can use chunks_exact() to leave out the last chunk if it is not full.
For this particular situation, chunks_exact is what you want to use right now.
For the more general case of "taking all but the last n items of an iterator", Itertools has a method just for this: dropping_back.
Note that unlike methods of std::iter::Iterator, dropping_back is evaluated eagerly. Take care if your iterator chain is computation-heavy.
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]
}
In the Kotlin collection functions, we have
setOf, mutableSetOf, sortedSetOf, linkedSetOf and hashSetOf.
The first 3 is understood their differences. But what's the last two?
How are they different from others and from themselves?
You know that linkedSetOf returns a LinkedHashSet whereas hashSetOf returns a HashSet. The difference between LinkedHashSet and HashSet is that LinkedHashSet maintains the order of elements with which they were either inserted into or removed from the set whereas HashSet doesn't maintain the order.
Observe the example below (Kotlin playground, if you want to see it in action)
val set: LinkedHashSet<Int> = linkedSetOf(1, 3, 2)
println(set) // [1, 3, 2]
set.remove(3)
set += listOf(5, 4)
println(set) // [1, 2, 5, 4]
val hashSet: HashSet<Int> = hashSetOf(1, 3, 2)
println(hashSet) // [1, 2, 3]
hashSet.remove(3)
hashSet += listOf(5, 4)
println(hashSet) // [1, 2, 4, 5]
If you look at how the set is printed in different instances of addition and removal of elements, you would see that in the case of LinkedHashSet, the order in which the elements were added or removed is maintained and printed in the same order when iterated over.
Note: Just to show that HashSet doesn't sort the elements, below is an example (Kotlin playground)
val linkedHashSet = linkedSetOf("aaa","bbb","ccc")
println(linkedHashSet) // [aaa, bbb, ccc]
linkedHashSet.remove("ccc")
linkedHashSet += listOf("ddd","zzz")
println(linkedHashSet) // [aaa, bbb, ddd, zzz]
val hashSet = hashSetOf("aaa","bbb","ccc")
println(hashSet) // [aaa, ccc, bbb]
hashSet.remove("ccc")
hashSet += listOf("ddd","zzz")
println(hashSet) // [aaa, bbb, zzz, ddd]
linkedSetOf returns a LinkedHasSet
hasSetOf returns a HashSet
The main difference between HashSet and LinkedHasSet is that LinkedHasSet maintains insertion order (order in which elements are inserted into LinkedHashSet). On the other hand HashSet doesn't maintain any order.
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.
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