Zip 3 lists of equal length - kotlin

Is there a standard operation in Kotlin stdlib which would allow to iterate over a zip of 3 (or more) lists?
Effectively it should do:
list1.zip(list2).zip(list3) { (a, b), c -> listOf(a, b, c)}

Here are functions in the style of the standard library that do this. I'm not saying these are particularly optimized, but I think they're at least easy to understand.
/**
* Returns a list of lists, each built from elements of all lists with the same indexes.
* Output has length of shortest input list.
*/
public inline fun <T> zip(vararg lists: List<T>): List<List<T>> {
return zip(*lists, transform = { it })
}
/**
* Returns a list of values built from elements of all lists with same indexes using provided [transform].
* Output has length of shortest input list.
*/
public inline fun <T, V> zip(vararg lists: List<T>, transform: (List<T>) -> V): List<V> {
val minSize = lists.map(List<T>::size).min() ?: return emptyList()
val list = ArrayList<V>(minSize)
val iterators = lists.map { it.iterator() }
var i = 0
while (i < minSize) {
list.add(transform(iterators.map { it.next() }))
i++
}
return list
}
Usage:
val list1 = listOf(1, 2, 3, 4)
val list2 = listOf(5, 6)
val list3 = listOf(7, 8, 9)
println(zip(list1, list2, list3)) // [[1, 5, 7], [2, 6, 8]]

Related

How can I make a generic function that works for all subclasses of a collection and at the same time accepts the parameters correctly?

// Generic Function but not work as expecting
inline fun <reified C : Collection<T>, T> C.dropElements(word: T): C {
return when (this) {
is Set<*> -> (this - word) as C
is List<*> -> filter { it != word } as C
else -> throw Exception("I can't implement all out of Collection types")
}
}
fun main() {
val originalList: List<String> = readln().split(" ")
val originalSet: Set<String> = originalList.toSet()
val word: String = readln()
val dropElements1: List<String> = originalList.dropElements(word).also(::println)
val dropElements2: Set<String> = originalSet.dropElements(word).also(::println)
// Incorrect: Int and String are different types
val dropElements3: List<Int> = listOf(1, 2, 3).dropElements(word).also(::println)
}
Is the question about the fact that the following line compiles?
listOf(1, 2, 3).dropElements(word)
If so, then what the compiler is doing is inferring these types:
listOf(1, 2, 3).dropElements<List<Int>, Any>(word)
This is possible because the type parameter in List is covariant, i.e. it is defined as List<out E>. This means that a List<Int> is also a List<Any>.
Doc about generics and variance here.
Your function is working as I would expect.
I think you are expected the integers in dropElements3 to reduce with word, but the problem is that readln() is returning a String, so an integer is not matching the String representation of the same Here is your original code (using kotest library to assert the answers)
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
class ATest {
inline fun <reified C : Collection<T>, T> C.dropElements(word: T): C {
return when (this) {
is Set<*> -> (this - word) as C
is List<*> -> filter { it != word } as C
else -> throw Exception("I can't implement all out of Collection types")
}
}
#Test
fun main() {
val originalList: List<String> = listOf("fish","dog","cat","bird")
val originalSet: Set<String> = originalList.toSet()
var word = "cat"
val dropElements1: List<String> = originalList.dropElements(word).also(::println)
dropElements1 shouldBe listOf("fish","dog","bird")
val dropElements2: Set<String> = originalSet.dropElements(word).also(::println)
dropElements2 shouldBe listOf("fish","dog","bird")
var dropElements3: List<Int> = listOf(1, 2, 3).dropElements(word).also(::println)
dropElements3 shouldBe listOf(1, 2, 3)
word = "2"
dropElements3 = listOf(1, 2, 3).dropElements(word).also(::println)
dropElements3 shouldBe listOf(1, 2, 3) // integer 2 != "2" no dropped elements
var word2 = 2 // now this is an integer
dropElements3 = listOf(1, 2, 3).dropElements(word2).also(::println)
dropElements3 shouldBe listOf(1, 3)
}
}
The List filter and Set - operations are removing an object based on a equality test on members (and hashcode too for the Set). How can Kotlin/Java know you want to treat integers as like Strings?
The only way you can solve this is to decide how to transform integers into strings (or visa versa). Of course there are multiple string representations of integers - decimal, hexadecimal, and so on...
I think T is a covariant type parameter with the upper bound T: comparable is a great deal!
data class Koko(val name: String) : Comparable<Koko> {
override fun compareTo(other: Koko) = name.compareTo(other.name)
}
inline fun <reified C : Iterable<T>, reified T : Comparable<T>> C.dropElements(word: T): C {
return when (this) {
is Set<*> -> (this - word) as C
is List<*> -> (this.filter { it != word }).toList<T>() as C
else -> throw Exception("I can't implement all out of Collection types")
}
}
fun main() {
val list: List<String> = listOf("apple", "banana", "orange")
val set: Set<String> = list.toSet()
val mutableList: MutableList<String> = list.toMutableList()
val listOfKoko: List<Koko> = List<Koko>(5) { Koko("Name$it") }
val mapOfKoko: Map<Int, String> = list.withIndex().associate { it.index to it.value }
val dropElements1: List<String> = list.dropElements("apple").also(::println)
val dropElements2: Set<String> = set.dropElements("apple").also(::println)
val dropElements3: List<Koko> = listOfKoko.dropElements(Koko("Name1")).also(::println)
val dropElements4: MutableList<String> = mutableList.dropElements("apple").also(::println)
// Incorrect: different types ot not is Iterable
val dropElements5: List<String> = list.dropElements(1).also(::println)
val dropElements6: List<Int> = listOf(1, 2, 3).dropElements("apple").also(::println)
val dropElements7: Map<Int, String> = mapOfKoko.dropElements(Koko("Name1")).also(::println)
}

Kotlin how apply different functions by predictor

I need to apply 2 different functions to my list in Kotlin based on external map, saving initial order of the list.
class Example(){
val mapper = mapOf<String, Int>(
"abc" to 1,
"de" to 0,
"fedd" to 1,
"q" to 1
)
fun function1(x:String)=x.length*2
fun function0(x:String)=x.length*3
fun function(lst: List<String>){
TODO("Need to apply function1 for all values in dictionary, where corresponding value in mapper is 1" +
"And apply function 0 for all values where corresponding value in mapper is 0")
}
}
So, for list ["q", "fedd", "de"] method Example.function() should return [2, 8, 6]
2 -> q value in mapper is 1, applying function1, multiplying by 2, get 1*2=2
8 -> fedd in mapper is 1, applying function1, multiplying by 2, get 4*2=8
6 -> de in mapper is 0, applying function0, multiplying by 3, get 2*3=6
What is the easiest way to realise this logic?
You could use map on the list to achieve this. follow below:
val mapper = mapOf<String, Int>(
"abc" to 1,
"de" to 0,
"fedd" to 1,
"q" to 1
)
fun function1(x: String) = x.length * 2
fun function0(x: String) = x.length * 3
fun function(lst: List<String>) {
val newList = lst.map {
when (mapper[it]) {
0 -> function0(it)
1 -> function1(it)
else -> error("Key doesn't exist in map, throw or return value as is.")
}
}
}

Kotlin: mutable map of mutable list won't update the list

(Kotlin newbie here) I have a text file with rows that look like these:
1-1-1
1-1-2
1-1-3
2-1-1
2-1-2
etc.
I have to transform these data to a map where the key is the first 2 elements and the value is a list of the third elements that that match the key. For example, the above records will transform into this JSON:
1-1: [1, 2, 3]
2-1: [1, 2]
etc.
I'm unable to increment the list. Here's a simplified version, I get stuck on the "else":
fun main () {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, List<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
if (m[key] == null) m[key] = listOf(c[2].toInt())
else println("How do I append to the list?")
}
println(m)
}
Output:
{1-1=[1], 2-1=[1]}
But I want:
{1-1=[1, 2, 3], 2-1=[1, 2]}
Thank you (comments about idiomatic form are welcome!)
If we continue to follow your strategy, what you need is for the value type to be a MutableList. Then you can add to the existing MutableList when there's already an existing list for that key:
fun main() {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, MutableList<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
if (m[key] == null) m[key] = mutableListOf(c[2].toInt())
else m[key]!!.add(c[2].toInt())
}
println(m)
}
This can be more natural using getOrPut(). It returns the existing MutableList or creates one and puts it in the map if it's missing. Then we don't have to deal with if/else, and can simply add the new item to the list.
fun main() {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, MutableList<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
m.getOrPut(key, ::mutableListOf).add(c[2].toInt())
}
println(m)
}
But we can use the map and groupBy functions to create it more simply:
val m = l1.map { it.split("-") }
.groupBy(
{ "${it[0]}-${it[1]}" }, // keys
{ it[2].toInt() } // values
)
You can achieve your desired output with a single call to groupBy of the Kotlin standard library.
val input = listOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val result = input.groupBy(
{ it.substringBeforeLast("-") }, // extract key from item
{ it.substringAfterLast("-").toInt() } // extract value from item
)
The first lambda function extracts the key to group by of every list item. The second lambda function provides the value to use for each list item.
You can also do it by first mapping your values to Pairs and then group them as follows:
fun main(args: Array<String>) {
val input = listOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val result = input.map {
val values = it.split("-")
"${values[0]}-${values[1]}" to values[2]
}.groupBy ({ it.first }) { it.second }
println(result)
}

How can I take varying chunks out of a Kotlin Sequence?

If I have a Kotlin sequence, every invocation of take(n) restarts the sequence.
val items = generateSequence(0) {
if (it > 9) null else it + 1
}
#Test fun `take doesn't remember position`() {
assertEquals(listOf(0, 1), items.take(2).toList())
assertEquals(listOf(0, 1, 2), items.take(3).toList())
}
Is there an easy way of write say, another(n) such that
#Test fun `another does remember position`() {
assertEquals(listOf(0, 1), items.another(2).toList())
assertEquals(listOf(2, 3, 4), items.another(3).toList())
}
I suppose that I have to have something that isn't the Sequence to keep the state, so maybe what I'm actually asking for is a nice definition of fun Iterator<T>.another(count: Int): List<T>
Sequence does not remember its position, but its iterator does remember:
val iterator : Iterator<Int> = items.iterator()
Now all you need is something like take(n) but for Iterator<T>:
public fun <T> Iterator<T>.another(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
list.add(item)
if (++count == n)
break
}
return list
}
What about this:
#Test
fun `another does remember position`() {
val items: Sequence<Int> = generateSequence(0) {
if (it > 9) null else it + 1
}
val (first, rest) = items.another(2)
assertEquals(listOf(0, 1), first.toList())
assertEquals(listOf(2, 3, 4), rest.another(3).first.toList())
}
fun <T> Sequence<T>.another(n: Int): Pair<Sequence<T>, Sequence<T>> {
return this.take(n) to this.drop(n)
}
To answer the last part of your question:
I suppose that I have to have something that isn't the Sequence to keep the state, so maybe what I'm actually asking for is a nice definition of fun Iterator.another(count: Int): List
One such implementation would be:
fun <T> Iterator<T>.another(count: Int): List<T> {
val collectingList = mutableListOf<T>()
while (hasNext() && collectingList.size < count) {
collectingList.add(next())
}
return collectingList.toList()
}
This passes your test if you use the iterator produced by the sequence:
#Test
fun `another does remember position`() {
val items = generateSequence(0) {
if (it > 9) null else it + 1
}.iterator() //Use the iterator of this sequence.
assertEquals(listOf(0, 1), items.another(2))
assertEquals(listOf(2, 3, 4), items.another(3))
}
To me what you've described is an iterator, since it's something that allows you to go over a collection or sequence etc. but also remember its last position.
NB the implementation above wasn't written to take into consideration what should happen for non-positive counts passed in, and if the count is larger than what's left to iterate over you'll be returned a list which has smaller size than n. I suppose you could consider this an exercise for yourself :-)
Sequence does not remember its position, but its iterator does remember:
val iterator : Iterator<Int> = items.iterator()
Unfortunately there is no take(n) for an iterator, so to use the one from stdlib you need to wrap iter into an Iterable:
val iterable : Iterable<Int> = items.iterator().asIterable()
fun <T> Iterator<T>.asIterable() : Iterable<T> = object : Iterable<T> {
private val iter = this#asIterable
override fun iterator() = iter
}
That makes itareble.take(n) remember its position, but unfortunately there is a of-by-one error because the standard .take(n) asks for one element too many:
public fun <T> Iterable<T>.take(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
if (count++ == n)
break
list.add(item)
}
return list.optimizeReadOnlyList()
}
That can be fixed with a little tweak:
public fun <T> Iterable<T>.take2(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
list.add(item)
//count++
if (++count == n)
break
}
return list
}
Now both of you tests pass:
#Test fun `take does not remember position`() {
assertEquals(listOf(0, 1), items.take2(2).toList())
assertEquals(listOf(0, 1, 2), items.take2(3).toList())
}
#Test fun `another does remember position`() {
assertEquals(listOf(0, 1), iter.take2(2).toList())
assertEquals(listOf(2, 3, 4), iter.take2(3).toList())
}
You could create a function generateStatefulSequence which creates a sequence which keeps its state by using a second sequence's iterator to provide the values.
The iterator is captured in the closure of that function.
On each iteration the seed lambda ({ i.nextOrNull() }) of the returned sequence starts off with the next value provided by the iterator.
// helper
fun <T> Iterator<T>.nextOrNull() = if(hasNext()) { next() } else null
fun <T : Any> generateStatefulSequence(seed: T?, nextFunction: (T) -> T?): Sequence<T> {
val i = generateSequence(seed) {
nextFunction(it)
}.iterator()
return generateSequence(
seedFunction = { i.nextOrNull() },
nextFunction = { i.nextOrNull() }
)
}
Usage:
val s = generateStatefulSequence(0) { if (it > 9) null else it + 1 }
println(s.take(2).toList()) // [0, 1]
println(s.take(3).toList()) // [2, 3, 4]
println(s.take(10).toList()) // [5, 6, 7, 8, 9, 10]
Try it out
Here is a nice definition of fun Iterator<T>.another(count: Int): List<T> as requested:
fun <T> Iterator<T>.another(count: Int): List<T> =
if (count > 0 && hasNext()) listOf(next()) + this.another(count - 1)
else emptyList()
As another workaround (similar to the suggestion by Willi Mentzel above) would be to create a asStateful() extension method that converts any sequence into a one that will remember the position, by wrapping it into an Iterable that always yields the same iterator.
class StatefulIterable<out T>(wrapped: Sequence<T>): Iterable<T> {
private val iterator = wrapped.iterator()
override fun iterator() = iterator
}
fun <T> Sequence<T>.asStateful(): Sequence<T> = StatefulIterable(this).asSequence()
Then you can do:
val items = generateSequence(0) {
if (it > 9) null else it + 1
}.asStateful()
#Test fun `stateful sequence does remember position`() {
assertEquals(listOf(0, 1), items.take(2).toList())
assertEquals(listOf(2, 3, 4), items.take(3).toList())
}
Try it here: https://pl.kotl.in/Yine8p6wn

Kotlin: Convert large List to sublist of set partition size

I'm looking for a function equivalent to Groovy's collate which would partition a large List into batches for processing. I did see subList which could be adapted into a similar function but wanted to check and make sure I wasn't missing an in-built or crazy simple alternative to rolling my own.
With Kotlin 1.3, according to your needs, you may choose one of the following ways to solve your problem.
#1. Using chunked
fun main() {
val list = listOf(2, 4, 3, 10, 8, 7, 9)
val newList = list.chunked(2)
//val newList = list.chunked(size = 2) // also works
print(newList)
}
/*
prints:
[[2, 4], [3, 10], [8, 7], [9]]
*/
#2. Using windowed
fun main() {
val list = listOf(2, 4, 3, 10, 8, 7, 9)
val newList = list.windowed(2, 2, true)
//val newList = list.windowed(size = 2, step = 2, partialWindows = true) // also works
println(newList)
}
/*
prints:
[[2, 4], [3, 10], [8, 7], [9]]
*/
NOTE: For Kotlin 1.2 and newer, please see the chunked and windowed functions that are now in the standard library. There is no need for a custom solution.
Here is an implementation of a lazy batching extension function which will take a collection, or anything that can become a Sequence and return a Sequence of List each of that size, with the last one being that size or smaller.
Example usage to iterate a list as batches:
myList.asSequence().batch(5).forEach { group ->
// receive a Sequence of size 5 (or less for final)
}
Example to convert batches of List to Set:
myList.asSequence().batch(5).map { it.toSet() }
See the first test case below for showing the output given specific input.
Code for the function Sequence<T>.batch(groupSize):
public fun <T> Sequence<T>.batch(n: Int): Sequence<List<T>> {
return BatchingSequence(this, n)
}
private class BatchingSequence<T>(val source: Sequence<T>, val batchSize: Int) : Sequence<List<T>> {
override fun iterator(): Iterator<List<T>> = object : AbstractIterator<List<T>>() {
val iterate = if (batchSize > 0) source.iterator() else emptyList<T>().iterator()
override fun computeNext() {
if (iterate.hasNext()) setNext(iterate.asSequence().take(batchSize).toList())
else done()
}
}
}
Unit tests proving it works:
class TestGroupingStream {
#Test fun testConvertToListOfGroupsWithoutConsumingGroup() {
val listOfGroups = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).asSequence().batch(2).toList()
assertEquals(5, listOfGroups.size)
assertEquals(listOf(1,2), listOfGroups[0].toList())
assertEquals(listOf(3,4), listOfGroups[1].toList())
assertEquals(listOf(5,6), listOfGroups[2].toList())
assertEquals(listOf(7,8), listOfGroups[3].toList())
assertEquals(listOf(9,10), listOfGroups[4].toList())
}
#Test fun testSpecificCase() {
val originalStream = listOf(1,2,3,4,5,6,7,8,9,10)
val results = originalStream.asSequence().batch(3).map { group ->
group.toList()
}.toList()
assertEquals(listOf(1,2,3), results[0])
assertEquals(listOf(4,5,6), results[1])
assertEquals(listOf(7,8,9), results[2])
assertEquals(listOf(10), results[3])
}
fun testStream(testList: List<Int>, batchSize: Int, expectedGroups: Int) {
var groupSeenCount = 0
var itemsSeen = ArrayList<Int>()
testList.asSequence().batch(batchSize).forEach { groupStream ->
groupSeenCount++
groupStream.forEach { item ->
itemsSeen.add(item)
}
}
assertEquals(testList, itemsSeen)
assertEquals(groupSeenCount, expectedGroups)
}
#Test fun groupsOfExactSize() {
testStream(listOf(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15), 5, 3)
}
#Test fun groupsOfOddSize() {
testStream(listOf(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18), 5, 4)
testStream(listOf(1,2,3,4), 3, 2)
}
#Test fun groupsOfLessThanBatchSize() {
testStream(listOf(1,2,3), 5, 1)
testStream(listOf(1), 5, 1)
}
#Test fun groupsOfSize1() {
testStream(listOf(1,2,3), 1, 3)
}
#Test fun groupsOfSize0() {
val testList = listOf(1,2,3)
val groupCountZero = testList.asSequence().batch(0).toList().size
assertEquals(0, groupCountZero)
val groupCountNeg = testList.asSequence().batch(-1).toList().size
assertEquals(0, groupCountNeg)
}
#Test fun emptySource() {
listOf<Int>().asSequence().batch(1).forEach { groupStream ->
fail()
}
}
}
A more simplistic/functional-style solution would be
val items = (1..100).map { "foo_${it}" }
fun <T> Iterable<T>.batch(chunkSize: Int) =
withIndex(). // create index value pairs
groupBy { it.index / chunkSize }. // create grouping index
map { it.value.map { it.value } } // split into different partitions
items.batch(3)
Note 1: Personally I'd prefer partition as a method name here, but it's already present in Kotlin's stdlib to separate a lists into 2 parts given a predicate.
Note 2: The the iterator solution from Jayson may scale better than this solution for large collections.
In Kotlin 1.2 M2 and later you can use chunked and windowed (see Kotlin 1.2 M2 is out | Kotlin Blog). Note that there are Sequence variances too (see kotlin.sequences - Kotlin Programming Language).
For versions of Kotlin prior to 1.2 M2 I recommend using Lists.partition(List, int) from google-guava (it uses java.util.List.subList(int, int)):
If you are unfamiliar with Guava see CollectionUtilitiesExplained ยท google/guava Wiki for more details.
You can create your own Kotlin extension function for it if you want:
fun <T> List<T>.collate(size: Int): List<List<T>> = Lists.partition(this, size)
If you want an extension function for mutable lists then in a separate Kotlin file (to avoid platform declaration clashes):
fun <T> MutableList<T>.collate(size: Int): List<MutableList<T>> = Lists.partition(this, size)
If you want something lazy loaded like in Jayson Minard's answer you can use Iterables.partition(Iterable, int). You might also be interested in Iterables.paddedPartition(Iterable, int) if you want to pad the last sublist if it is smaller than the specified size. These return Iterable<List<T>> (I don't see much point in making it Iterable<Iterable<T>> as subList returns an efficient view).
If for some reason you don't want to depend on Guava you can roll your own pretty easily using the subList function you mentioned:
fun <T> List<T>.collate(size: Int): List<List<T>> {
require(size > 0)
return if (isEmpty()) {
emptyList()
} else {
(0..lastIndex / size).map {
val fromIndex = it * size
val toIndex = Math.min(fromIndex + size, this.size)
subList(fromIndex, toIndex)
}
}
}
or
fun <T> List<T>.collate(size: Int): Sequence<List<T>> {
require(size > 0)
return if (isEmpty()) {
emptySequence()
} else {
(0..lastIndex / size).asSequence().map {
val fromIndex = it * size
val toIndex = Math.min(fromIndex + size, this.size)
subList(fromIndex, toIndex)
}
}
}
Dummy Array
for (i in 0..49){
var data="java"
}
array.add(data)
Used:
var data=array?.chunked(15)
kotlin's method
There is unfortunately no built-in function for that yet and while functional and Sequence-based implementations from other answers look nice, if you just need is List of Lists, I'd suggest writing a little bit of ugly, imperative, but performant code.
This is my final result:
fun <T> List<T>.batch(chunkSize: Int): List<List<T>> {
if (chunkSize <= 0) {
throw IllegalArgumentException("chunkSize must be greater than 0")
}
val capacity = (this.size + chunkSize - 1) / chunkSize
val list = ArrayList<ArrayList<T>>(capacity)
for (i in 0 until this.size) {
if (i % chunkSize == 0) {
list.add(ArrayList(chunkSize))
}
list.last().add(this.get(i))
}
return list
}