How can I convert a Map<String, ArrayList<Int>> to a Map<String, Int>?
My objects are as follows:
val mapOfItems = mapOf("x" to listOf(1, 2), "y" to listOf(1, 3), "z" to listOf(2, 3))
I want to get an object like this:
val mapOfSummedItems = mapOf("x" to 3, "y" to 4, "z" to 5)
I tried:
mapOfItems.map { (key, value) -> SummedItems(key, value.sum()) }
.groupBy { it.key }
data class SummedItems(val key: String, val sum: Int)
That does not give me what I want that gives me an Map<String, ArrayList<SummedItems>>
.map() on a Map generates a List. While you can make this a List<Pair> and then use associate* operators to get back into a Map, you can also just map the values directly:
mapOfItems.mapValues { it.value.sum() }
I have two immutable maps, as example:
val a = mapOf("a" to 1, "z" to 1)
val b = mapOf("b" to 1, "a" to 1, "c" to 1)
Is there any elegant way to concatenate these maps and have a single map with all the keys? If it has duplicated keys, I want to replace it with value from B map.
The result should look something like:
mapOf("z" to 1, "b" to 1, "a" to 1, "c" to 1)
simple + operator:
/**
* Creates a new read-only map by replacing or adding entries to this map from another [map].
*
* The returned map preserves the entry iteration order of the original map.
* Those entries of another [map] that are missing in this map are iterated in the end in the order of that [map].
*/
public operator fun <K, V> Map<out K, V>.plus(map: Map<out K, V>): Map<K, V> =
LinkedHashMap(this).apply { putAll(map) }
example:
fun main(args: Array<String>) {
val a = mapOf("a" to 1, "z" to 1)
val b = mapOf("b" to 1, "a" to 2, "c" to 1)
val c = a+b
println(c)
}
output: {a=2, z=1, b=1, c=1}
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]]
Is there a way in Kotlin for doing the sum() operation on a filtered list of numbers, without actually filtering out the elements first?
I'm looking for something like this:
val nums = listOf<Long>(-2, -1, 1, 2, 3, 4)
val sum = nums.sum(it > 0)
You can make use of Iterable<T>.sumBy:
/**
* Returns the sum of all values produced by [selector] function applied to each element in the collection.
*/
public inline fun <T> Iterable<T>.sumBy(selector: (T) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
You can pass a function to it where the function transforms negative value to 0. So, it sums up all values in the list which is greater than 0 since adding 0 makes no effect to the result.
val nums = listOf<Long>(-2, -1, 1, 2, 3, 4)
val sum = nums.sumBy { if (it > 0) it.toInt() else 0 }
println(sum) //10
If you require a Long value back, you have to write an extension for Long just like Iterable<T>.sumByDouble.
inline fun <T> Iterable<T>.sumByLong(selector: (T) -> Long): Long {
var sum: Long = 0
for (element in this) {
sum += selector(element)
}
return sum
}
Then, the toInt() conversion can be taken away.
nums.sumByLong { if (it > 0) it else 0 }
As suggested by #Ruckus T-Boom, if (it > 0) it else 0 can be simplified using Long.coerceAtLeast() which returns the value itself or the given minimum value:
nums.sumByLong { it.coerceAtLeast(0) }
sumBy and sumByDouble are Deprecated from kotlin 1.5 . You can check those link.
Use sumOf to get sum on a List or Array
sumOf
Returns the sum of all values produced by selector function applied to each element in the collection or Array.
Example:
data class Order(
val id : String,
val price : Double
)
val orderList = ......
val sum = orderList.sumOf { it.price }
data class Product(val name: String, val quantity: Int) {
}
fun main(args: Array<String>) {
val productList = listOf(
Product("A", 100),
Product("B", 200),
Product("C", 300)
)
val totalPriceInList1: Int = productList.map { it.quantity }.sum()
println("sum(): " + totalPriceInList1)
val totalPriceInList2: Int = productList.sumBy { it.quantity }
println("sumBy(): " + totalPriceInList2)
}
this is the result of our code
sum(): 600
sumBy(): 600
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
}