How do I get the sums for all elements of two lists in Kotlin? - kotlin

In Kotlin I have two lists:
val x: List<Int> = listOf(1,2,3,4,5,6)
val y: List<Int> = listOf(2,3,4,5,6,7)
How do I get:
val z: List<Int> = 3,5,7,9,11,13
without using loops?

Assuming both list have the same size:
Using zip
val sum = x.zip(y) { xv, yv -> xv + yv }
Using simple map and mapIndexed
val sum = (0 until x.size).map { x[it] + y[it] }
// or
val sum = x.mapIndexed { index, xv -> xv + y[index] }
When the size can be different and you would assume 0 for out of range entries:
Using an array
val sum = IntArray(maxOf(x.size, y.size)) {
x.getOrElse(it, {0}) + y.getOrElse(it, {0})
}.toList()
Using range:
val sum = (0 until maxOf(x.size, y.size)).map {
x.getOrElse(it, {0}) + y.getOrElse(it, {0})
}
Extending the lists to same size
val xExtended = x + Array(maxOf(0, y.size - x.size), { 0 })
val yExtended = y + Array(maxOf(0, x.size - y.size), { 0 })
val sum = xExtended.zip(yExtended) { xv, yv -> xv + yv }

I'd go with a range and map:
val sums = (0 until x.size).map { x[it] + y[it] }
It's probably less overhead than zip.

Related

Find the list item of Pairs with the minimum value

val index = listOf("abc", "def", "ghi", "jkl", "mno")
.mapIndexed { index, v ->
var t = 0
var p = 0
for (s in v) {
t += ("deh".get(p++).toInt() - s.toInt()).absoluteValue
}
Pair(index, v)
}
.minOf {
val iterator = iterator<Pair<Int, String>>(it)
if (!iterator.hasNext()) throw NoSuchElementException()
var minValue = iterator.next().second
while (iterator.hasNext()) {
val v = selector(iterator.next())
minValue = minOf(minValue, v)
}
return minValue
}
This is an alternative solution and works, but I am wondering if the solution can be done using mapOf as shown above?
val index = listOf("abc", "def", "ghi", "jkl", "jad", "jaa", "mno")
.mapIndexed { index, v ->
var t = 0
var p = 0
for (s in v) {
t += ("jac".get(p++).toInt() - s.toInt()).absoluteValue
}
Pair(index, t)
}.toSortedSet(compareBy { it.second })
.first()
I create a map of Pairs and I want to find the index of the map item where the Pair with the value (the second item in the pair) is the lowest value (minimum) of all the pairs. If possible, I would like to use the minOf function. The first example above will not compile because of bugs in the minOf function. Not sure how to iterate over the map of Pairs.
You can use minBy {} to get the minimum value from a collection, although often it's safer to use minByOrNull {} in case no minimal value can be computed (which could happen if the list is empty).
import kotlin.math.absoluteValue
fun main() {
val minElement = listOf("abc", "def", "ghi", "jkl", "jad", "jaa", "mno")
.minByOrNull { v ->
var t = 0
var p = 0
for (s in v) {
t += ("jac".get(p++).toInt() - s.toInt()).absoluteValue
}
t
}
println(minElement)
}
jad
Run in Kotlin Playground
If you also want to find the index of the minimal value, then you can use withIndex(), which will pair each list element with its index.
import kotlin.math.absoluteValue
fun main() {
val minIndexedElement = listOf("abc", "def", "ghi", "jkl", "jad", "jaa", "mno")
.withIndex() // adds the index to each element
.minByOrNull { (_, v) ->
var t = 0
var p = 0
for (s in v) {
t += ("jac".get(p++).toInt() - s.toInt()).absoluteValue
}
t
}
println(minIndexedElement)
}
IndexedValue(index=4, value=jad)
Run in Kotlin Playground
Another solution would be to extract the character codes from "jar" and from each item, and then to zip the two code lists. zip allows for a transform closure in which the calculation with the two codes can be made. After that sum() gives the wanted value.
data class Result(val index: Int, val string: String, val computedValue: Int)
val list = listOf("abc", "def", "ghi", "jkl", "jad", "jaa", "mno")
val result = list
.mapIndexed { idx, str ->
val codes1 = "jac".toCharArray().map { it.code }
val codes2 = str.toCharArray().map { it.code }
val computed = codes1.zip(codes2) { code1, code2 -> (code1 - code2).absoluteValue }.sum()
Result(idx, str, computed)
}
.minByOrNull { it.computedValue }
println(result) // Output: Result(index=4, string=jad, computedValue=1)
Instead of the helper data class Result a Triple instance could be used:
...
Triple(idx, str, computed)
}
.minByOrNull { it.third }
// Output: (4, jad, 1)
Or if the calculated value is not needed, it could be dropped like that:
...
?.let { it.first to it.second }
// Output: (4, jad)

Optimise grouping 512x512 coordinates into 117 smaller, quartered chunks

I'm creating image map tiles for Leaflet.js based on data from a computer game. I'm processing the map data in Kotlin. A map tile server for Leaflet.js has to host image tiles at various zoom-levels, so I need to create them.
These are the resolutions I want to create, based on a source image of 512x512px.
512x512 pixels (zoomed out the most)
256x256 pixels
128x128 pixels
64x64 pixels
32x32 pixels (the most zoomed in)
A code example is at the bottom of this post.
I'm using groupBy at the moment, but the performance isn't great.
// for each possible chunk size...
ChunkSize.entries.flatMap { chunkSize ->
// and for each tile...
chunk.tiles.entries.groupBy(
// get the chunk the tile belongs to
{ (tile, _) -> tile.toChunkPosition(chunkSize) }
) { (tile, colour) ->
tile to colour
}.map { (chunkPosition, tiles) ->
// aggregate the grouped tiles into a map,
// and create a new chunk
Chunk(
tiles = tiles.toMap(),
size = chunkSize,
position = chunkPosition,
)
}
}
// this can take up to 0.5 seconds
It takes around 0.5 seconds to convert a 512x512px source image into
1 512x512px tile
4 256x256px tiles
16 128x128px tiles
32 64x64px tiles
64 32x32px tiles
I'd like to improve the performance.
Options
Sorting and chunking/windowing
Using windows won't be easy, because the data in the tiles isn't necessarily continuous. There might be gaps between some tiles.
Grouping
I've tried using Grouping, but I didn't note a significant difference. The lazy evaluation isn't useful here, and using a mutable map to try and improve the accumulation didn't help either.
ChunkSize.entries.flatMap { chunkSize ->
val grouped: Map<ChunkPosition, MutableMap<TilePosition, Colour>> =
chunk.tiles.entries.groupingBy { (tile, _) ->
tile.toChunkPosition(chunkSize)
}.fold(
initialValueSelector = { _, _ -> mutableMapOf() },
) { _, accumulator, (tilePosition, colour) ->
accumulator[tilePosition] = colour
accumulator
}
grouped.entries.map { (chunkPosition, tiles) ->
Chunk(
tiles = tiles,
size = chunkSize,
position = chunkPosition,
)
}
}
Optimise toChunkPosition?
The function for getting the chunk position for every tile, and it's using division, which can be slow.
fun TilePosition.toChunkPosition(chunkSize: ChunkSize) =
ChunkPosition(
floor(x.toDouble() / chunkSize.lengthInTiles.toDouble()).toInt(),
floor(y.toDouble() / chunkSize.lengthInTiles.toDouble()).toInt(),
)
Coroutines
I'm open to using coroutines, so work can be done in parallel, but first I want to optimise the existing code.
Full code
This is a simplified example. The chunk sizes have been reduced to 1, 2, 4, 8, and 16 pixels.
import kotlin.math.floor
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.time.measureTimedValue
val sourceChunk = Chunk(
size = ChunkSize.MAX,
position = ChunkPosition(0, 0),
// create some dummy test data
tiles = listOf(
"0000000000000088",
"1111111110000088",
"0000000000000088",
"0000000222722288",
"0090000000700000",
"3393333330700000",
"0090000000700000",
"0090000444744444",
"0090000000700000",
"5595555000700000",
"0090000000000000",
"0090000066666666",
).flatMapIndexed { y, row ->
row.mapIndexed { x, colour ->
TilePosition(x, y) to Colour("$colour")
}
}.toMap()
)
fun main() {
println("Source chunk")
printChunk(sourceChunk)
println("-------")
val (chunks, time) = measureTimedValue {
subdivideChunk(sourceChunk)
}
chunks.forEach {
println("-------")
printChunk(it)
}
println("-------")
println("took: $time")
}
fun subdivideChunk(chunk: Chunk): List<Chunk> {
return ChunkSize.entries.flatMap { chunkSize ->
val grouped: Map<ChunkPosition, MutableMap<TilePosition, Colour>> =
chunk.tiles.entries.groupingBy { (tile, _) ->
tile.toChunkPosition(chunkSize)
}.fold(
initialValueSelector = { _, _ -> mutableMapOf() },
) { _, accumulator, (tilePosition, colour) ->
accumulator[tilePosition] = colour
accumulator
}
grouped.entries.map { (chunkPosition, tiles) ->
Chunk(
tiles = tiles,
size = chunkSize,
position = chunkPosition,
)
}
chunk.tiles.entries.groupBy(
{ (tile, _) -> tile.toChunkPosition(chunkSize) }
) { (tile, colour) ->
tile to colour
}.map { (chunkPosition, tiles) ->
Chunk(
tiles = tiles.toMap(),
size = chunkSize,
position = chunkPosition,
)
}
chunk.tiles.entries
.groupingBy { (tile, _) ->
tile.toChunkPosition(chunkSize)
}.fold(mutableMapOf<TilePosition, Colour>()) { accumulator, (tilePosition, colour) ->
accumulator += tilePosition to colour
accumulator
}.map { (chunkPosition, tiles) ->
Chunk(
tiles = tiles,
size = chunkSize,
position = chunkPosition,
)
}
}
}
fun printChunk(chunk: Chunk) {
println("chunk ${chunk.position} ${chunk.size}")
val minX = chunk.tiles.keys.minOf { it.x }
val minY = chunk.tiles.keys.minOf { it.y }
val maxX = chunk.tiles.keys.maxOf { it.x }
val maxY = chunk.tiles.keys.maxOf { it.y }
(minY..maxY).forEach { y ->
(minX..maxX).forEach { x ->
print(chunk.tiles[TilePosition(x, y)]?.rgba ?: " ")
}
println()
}
}
data class Chunk(
val tiles: Map<TilePosition, Colour>,
val size: ChunkSize,
val position: ChunkPosition,
) {
val topLeftTile: TilePosition = position.toTilePosition(size)
val bottomRightTile: TilePosition = TilePosition(
x = topLeftTile.x + size.lengthInTiles - 1,
y = topLeftTile.y + size.lengthInTiles - 1,
)
val xTileRange = topLeftTile.x..bottomRightTile.x
val yTileRange = topLeftTile.y..bottomRightTile.y
operator fun contains(tilePosition: TilePosition): Boolean =
tilePosition.x in xTileRange && tilePosition.y in yTileRange
}
data class Colour(val rgba: String)
data class TilePosition(val x: Int, val y: Int)
fun TilePosition.toChunkPosition(chunkSize: ChunkSize) =
ChunkPosition(
floor(x.toDouble() / chunkSize.lengthInTiles.toDouble()).toInt(),
floor(y.toDouble() / chunkSize.lengthInTiles.toDouble()).toInt(),
)
data class ChunkPosition(val x: Int, val y: Int)
fun ChunkPosition.toTilePosition(chunkSize: ChunkSize) =
TilePosition(
x * chunkSize.lengthInTiles,
y * chunkSize.lengthInTiles,
)
enum class ChunkSize(
val zoomLevel: Int,
) : Comparable<ChunkSize> {
CHUNK_512(-1),
CHUNK_256(0),
CHUNK_128(1),
CHUNK_064(2),
CHUNK_032(3),
;
/** 1, 2, 4, 8, or 16 */
val lengthInTiles: Int = 2f.pow(3 - zoomLevel).roundToInt()
companion object {
val entries: Set<ChunkSize> = values().toSet()
val MAX: ChunkSize = entries.maxByOrNull { it.lengthInTiles }!!
val MIN: ChunkSize = entries.minByOrNull { it.lengthInTiles }!!
}
}
I resolved this with 3 fixes
I didn't need to create sub-tiles for small zoom levels.
Instead of calculating the chunk position for each tile, I instead calculated the position once and then verified if the remaining tiles was within this chunk a 'chunk boundary'
I refactored so that instead of splitting a large chunk into smaller chunks, I built up larger chunks from smaller chunks
The result is twice as fast, when using my basic example. In the full-fat code with real data it's significantly faster, and that's without any further improvements (e.g. using coroutines to process
in parallel).
CSS render pixelated
Originally I created smaller tiles because the tiles became blurry as I zoomed in.
However, the original map data is all just pixels. Zooming in doesn't increase the amount of pixels.
If you open up the source image in a basic image editor, like Paint, and zoom in you'll see this
source image doesn't blur.
The tiles became blurry because my web browser was 'optimising' the images. Instead, I set the CSS
property image-rendering: pixelated
. This disables the optimisation.
Because of this I no longer need to create zoomed in tiles. Leaflet can instead dynamically scale
the larger images.
Group by chunk boundary
Instead of the 'expensive' toChunkPosition() used to determine if a tile is in a specific
chunk, I instead created a 'chunk boundary' that has two boundary points - 'left top' and 'right
bottom'.
data class ChunkPositionBounds(
val leftTop: TilePosition,
val rightBottom: TilePosition,
) : ClosedRange<TilePosition> {
constructor(chunkPosition: ChunkPosition, chunkSize: ChunkSize) : this(
chunkPosition.leftTopTile(chunkSize),
chunkPosition.rightBottomTile(chunkSize),
)
override val start: TilePosition
get() = leftTop
override val endInclusive: TilePosition
get() = rightBottom
}
I implemented the ClosedRange<MapTilePosition> so I can create a nice operator function
for MapChunkPosition
data class ChunkPosition(val x: Int, val y: Int) {
fun leftTopTile(chunkSize: ChunkSize): TilePosition =
TilePosition(
x * chunkSize.lengthInTiles,
y * chunkSize.lengthInTiles,
)
fun rightBottomTile(chunkSize: ChunkSize): TilePosition =
leftTopTile(chunkSize) + (chunkSize.lengthInTiles - 1)
}
Now given a chunk I can verify all its tiles have the correct position
/** Verify all [tiles][Chunk.tiles] are contained within [chunk] */
fun validateChunk(chunk: Chunk): Chunk {
val (validTiles, invalidTiles) = chunk.tiles.entries.partition { (tilePos, _) ->
tilePos in chunk
}
return if (invalidTiles.isNotEmpty()) {
println("WARNING Chunk $chunk contained ${invalidTiles.size}/${chunk.tiles.size} out-of-bounds tiles $invalidTiles")
chunk.copy(tiles = validTiles.associate { it.key to it.value })
} else {
chunk
}
}
Re-use previously grouped tiles
The final optimisation is that I only grouped tiles into a chunk in this per-tile basis once.
Once I was sure the input data was 'clean', I could re-use the chunked data to create larger
tiles.
(This optimisation is not so important given that smaller tiles are not needed thanks to the
css-rendering trick - but I'll describe it in case someone else finds this useful.)
/**
* Get all chunks of size [srcChunkSize] from [srcChunks], then for each source chunk
* create a new [ChunkPosition], based on [newChunkSize].
*
* Group the chunks by the new position, and merge the tiles of each chunk.
*/
fun regroupByChunkSize(
srcChunks: List<Chunk>,
srcChunkSize: ChunkSize,
newChunkSize: ChunkSize,
): List<Chunk> {
return srcChunks
.filter { it.size == srcChunkSize }
.groupBy { chunk ->
chunk.position
.leftTopTile(chunk.size)
.toChunkPosition(newChunkSize)
}.map { (newChunkPosition, chunks) ->
val tiles = chunks.flatMap {
it.tiles.entries
}.associate { (k, v) -> k to v }
Chunk(
tiles = tiles,
size = newChunkSize,
position = newChunkPosition,
)
}
}
I could then iteratively call regroupByChunkSize(...) to incrementally aggregate chunks.
fun aggregateChunks(
sourceChunks: List<Chunk>
): List<Chunk> {
val allGroupedChunks = mutableListOf<Chunk>()
val groupedChunks032 = sourceChunks.map { chunk -> validateChunk(chunk) }
allGroupedChunks += groupedChunks032
allGroupedChunks += regroupByChunkSize(allGroupedChunks, ChunkSize.CHUNK_032, ChunkSize.CHUNK_064)
allGroupedChunks += regroupByChunkSize(allGroupedChunks, ChunkSize.CHUNK_064, ChunkSize.CHUNK_128)
allGroupedChunks += regroupByChunkSize(allGroupedChunks, ChunkSize.CHUNK_128, ChunkSize.CHUNK_256)
allGroupedChunks += regroupByChunkSize(allGroupedChunks, ChunkSize.CHUNK_256, ChunkSize.CHUNK_512)
return allGroupedChunks.toList()
}
(This could be improved. Also, this example looks strange. The actual code I'm using is using Kafka,
so sourceChunks is a live updating list, and each regroupByChunkSize(...) call is an independent
Stream Task.)
Full example code
This code is very scruffy. I'm just using it for demo purposes.
If you want to use smaller chunk sizes, then change
val lengthInTiles: Int = 2f.pow(8 - zoomLevel).roundToInt()
to
val lengthInTiles: Int = 2f.pow(3 - zoomLevel).roundToInt()
import kotlin.math.floor
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.random.Random
import kotlin.random.nextInt
import kotlin.time.measureTimedValue
fun main() {
val sourceChunks = testData()
val (chunks, time) = measureTimedValue {
aggregateChunks(sourceChunks)
}
// chunks.forEach {
// println("-------")
// printChunk(it)
// }
// println("-------")
println("took: $time")
}
fun aggregateChunks(
sourceChunks: List<Chunk>
): List<Chunk> {
val allGroupedChunks = mutableListOf<Chunk>()
val groupedChunks032 = sourceChunks.map { chunk -> validateChunk(chunk) }
allGroupedChunks += groupedChunks032
allGroupedChunks += regroupByChunkSize(allGroupedChunks, ChunkSize.CHUNK_032, ChunkSize.CHUNK_064)
allGroupedChunks += regroupByChunkSize(allGroupedChunks, ChunkSize.CHUNK_064, ChunkSize.CHUNK_128)
allGroupedChunks += regroupByChunkSize(allGroupedChunks, ChunkSize.CHUNK_128, ChunkSize.CHUNK_256)
allGroupedChunks += regroupByChunkSize(allGroupedChunks, ChunkSize.CHUNK_256, ChunkSize.CHUNK_512)
return allGroupedChunks.toList()
}
/** Verify all [tiles][Chunk.tiles] are contained within [chunk] */
fun validateChunk(chunk: Chunk): Chunk {
val (validTiles, invalidTiles) = chunk.tiles.entries.partition { (tilePos, _) ->
tilePos in chunk
}
return if (invalidTiles.isNotEmpty()) {
println("WARNING Chunk $chunk contained ${invalidTiles.size}/${chunk.tiles.size} out-of-bounds tiles $invalidTiles")
chunk.copy(tiles = validTiles.associate { it.key to it.value })
} else {
chunk
}
}
/**
* Get all chunks of size [srcChunkSize] from [srcChunks], then for each source chunk
* create a new [ChunkPosition], based on [newChunkSize].
*
* Group the chunks by the new position, and merge the tiles of each chunk.
*/
fun regroupByChunkSize(
srcChunks: List<Chunk>,
srcChunkSize: ChunkSize,
newChunkSize: ChunkSize,
): List<Chunk> {
return srcChunks
.filter { it.size == srcChunkSize }
.groupBy { chunk ->
chunk.position
.leftTopTile(chunk.size)
.toChunkPosition(newChunkSize)
}.map { (newChunkPosition, chunks) ->
val tiles = chunks.flatMap {
it.tiles.entries
}.associate { (k, v) -> k to v }
Chunk(
tiles = tiles,
size = newChunkSize,
position = newChunkPosition,
)
}
}
fun printChunk(chunk: Chunk) {
println("chunk ${chunk.position} ${chunk.size}")
val minX = chunk.tiles.keys.minOf { it.x }
val minY = chunk.tiles.keys.minOf { it.y }
val maxX = chunk.tiles.keys.maxOf { it.x }
val maxY = chunk.tiles.keys.maxOf { it.y }
(minY..maxY).forEach { y ->
(minX..maxX).forEach { x ->
print(chunk.tiles[TilePosition(x, y)]?.rgba ?: " ")
}
println()
}
}
data class Chunk(
val tiles: Map<TilePosition, Colour>,
val size: ChunkSize,
val position: ChunkPosition,
) {
val bounds: ChunkPositionBounds by lazy {
ChunkPositionBounds(position, size)
}
operator fun contains(tilePosition: TilePosition): Boolean {
return tilePosition in bounds
}
}
data class ChunkPositionBounds(
val leftTop: TilePosition,
val rightBottom: TilePosition,
) : ClosedRange<TilePosition> {
constructor(chunkPosition: ChunkPosition, chunkSize: ChunkSize) : this(
chunkPosition.leftTopTile(chunkSize),
chunkPosition.rightBottomTile(chunkSize),
)
override val start: TilePosition
get() = leftTop
override val endInclusive: TilePosition
get() = rightBottom
}
data class Colour(val rgba: String)
data class TilePosition(val x: Int, val y: Int) : Comparable<TilePosition> {
override operator fun compareTo(other: TilePosition): Int =
when {
other.x != x -> x.compareTo(other.x)
else -> y.compareTo(other.y)
}
operator fun plus(addend: Int) =
TilePosition(x + addend, y + addend)
fun toChunkPosition(chunkSize: ChunkSize) =
ChunkPosition(
floor(x.toDouble() / chunkSize.lengthInTiles.toDouble()).toInt(),
floor(y.toDouble() / chunkSize.lengthInTiles.toDouble()).toInt(),
)
}
data class ChunkPosition(val x: Int, val y: Int) {
fun leftTopTile(chunkSize: ChunkSize): TilePosition =
TilePosition(
x * chunkSize.lengthInTiles,
y * chunkSize.lengthInTiles,
)
fun rightBottomTile(chunkSize: ChunkSize): TilePosition =
leftTopTile(chunkSize) + (chunkSize.lengthInTiles - 1)
}
enum class ChunkSize(
val zoomLevel: Int,
) : Comparable<ChunkSize> {
CHUNK_512(-1),
CHUNK_256(0),
CHUNK_128(1),
CHUNK_064(2),
CHUNK_032(3),
;
/** 1, 2, 4, 8, or 16 */
val lengthInTiles: Int = 2f.pow(8 - zoomLevel).roundToInt()
companion object {
val entries: Set<ChunkSize> = values().toSet()
val MAX: ChunkSize = entries.maxByOrNull { it.lengthInTiles }!!
val MIN: ChunkSize = entries.minByOrNull { it.lengthInTiles }!!
}
}
fun testData(): List<Chunk> {
val r = Random(1)
val src = List(x.ChunkSize.MAX.lengthInTiles) {
List(x.ChunkSize.MAX.lengthInTiles) { r.nextInt(0..9).toString() }
}
return src.flatMapIndexed { y, row ->
row.mapIndexed { x, colour ->
val tilePos = TilePosition(x, y)
val tileColour = Colour("$colour")
Chunk(
tiles = mapOf(tilePos to tileColour),
size = ChunkSize.MIN,
position = tilePos.toChunkPosition(ChunkSize.MIN),
)
}
}
}

Kotlin Integer.MAX_VALUE Returns Negative Number

Expected
Use Integer.MAX_VALUE in order to consistently return a large number for the purposes of comparison.
Observed
Integer.MAX_VALUE is returning a negative number.
Implement
In the sample code values are saved into a 2D table in order to find the minimum amount of coins required to make up a given amount.
Using Integer.MAX_VALUE
-2147483647 is being derived from Integer.MAX_VALUE.
fun main() {
// Steps - Iterative/bottom-up
// 1. Create a 2D table: Rows = Denominations(Denoms), Columns = Amount(Amt)
// 2. Store min # of coins in at [R][C] = Min(currentDenomMin, previousDenomMin)
// a. currentDenomMin = [R][C - coins.get(R)] + 1
// b. previousDenomMin = [R - 1][C]
// 3. Return minCount or -1 for table[coins.size - 1, Amt].
println("Min count: ${coinChange(intArrayOf(2), 3)}")
}
lateinit var table: Array<IntArray>
lateinit var mCoins: IntArray
private val maxValue = Integer.MAX_VALUE
fun coinChange(coins: IntArray, amt: Int): Int {
table = Array(coins.size, { IntArray(amt + 1) })
mCoins = coins
coins.sort()
buildMinCounts(amt)
val minCount = table[coins.size - 1][amt]
return if (minCount == maxValue) -1 else minCount
}
fun buildMinCounts(amt: Int) {
for (r in 0..mCoins.size - 1) {
for (c in 0..amt) {
val currentDenomValue = mCoins.get(r)
val currentDenomMin = getDenomMin(r, c - currentDenomValue) + 1
val previousDenomMin = getDenomMin(r - 1, c)
if (c == 0) {
table[r][c] = 0
} else table[r][c] = Math.min(currentDenomMin, previousDenomMin)
}
}
}
fun getDenomMin(r: Int, c: Int): Int {
if (r < 0 || c < 0) return maxValue
else return table[r][c]
}
fun printT(amt: Int) {
for (r in 0..mCoins.size - 1) {
for (c in 0..amt) {
print("${table[r][c]} ")
}
println("")
}
}
Using 999999999 as the maxValue instead
Works as expected.
fun main() {
println("Min count: ${coinChange(intArrayOf(2), 3)}")
}
lateinit var table: Array<IntArray>
lateinit var mCoins: IntArray
private val maxValue = 999999999
fun coinChange(coins: IntArray, amt: Int): Int {
table = Array(coins.size, { IntArray(amt + 1) })
mCoins = coins
coins.sort()
buildMinCounts(amt)
val minCount = table[coins.size - 1][amt]
return if (minCount == maxValue) -1 else minCount
}
fun buildMinCounts(amt: Int) {
for (r in 0..mCoins.size - 1) {
for (c in 0..amt) {
val currentDenomValue = mCoins.get(r)
val currentDenomMin = getDenomMin(r, c - currentDenomValue) + 1
val previousDenomMin = getDenomMin(r - 1, c)
if (c == 0) {
table[r][c] = 0
} else table[r][c] = Math.min(currentDenomMin, previousDenomMin)
}
}
}
fun getDenomMin(r: Int, c: Int): Int {
if (r < 0 || c < 0) return maxValue
else return table[r][c]
}
fun printT(amt: Int) {
for (r in 0..mCoins.size - 1) {
for (c in 0..amt) {
print("${table[r][c]} ")
}
println("")
}
}
It's because of overflow. getDenomMin(r, c - currentDenomValue) + 1 returns Integer.MAX_VALUE + 1 which causes overflow. There are two ways to avoid this:
Change maxValue to something such that it doesn't overflows and is actually is the maximum. For example, you have array of size 10^5 containing integers between 1 and 10^9. Now maximum possible sum will 10^5 * 10^9 which is 10^14 so we can set maxValue to any value greater than or equal to 10^14. In your case you can set it to something like 10^5 because you need count not sum which can be at max number of coins available.
val currentDenomMin = getDenomMin(r, c - currentDenomValue) + 1 Before adding 1 you can type it to Long so that it doesn't overflow.
val currentDenomMin = getDenomMin(r, c - currentDenomValue).toLong + 1

Kotlin: how to use reduce/fold with a data structure as the accumulative argument?

I want to use reduce/fold methods to create a new list from an existing List. This is the code I tried to write:
val list: List<Int> = listOf(1, 2, 3)
val newList = mutableListOf<Int>()
val sum: List<Int> = list.fold(newList) { (acc: List<Int>, i: Int) -> {
acc.add(i + 10)
acc
}}
It doesn't compile.
I want that newList will be (11,12,13).
How can I do it in a functional manner?
In Javascript, for example, this code compiles:
list.reduce((acc, item) => {
acc.push(item + 10)
return acc;
}, [])
The most convenient and functional way to do what you want is using map function:
val list: List<Int> = listOf(1, 2, 3)
val newList: MutableList<Int> = list.map { it + 10 }.toMutableList()
But if you really want to use fold for some (strange) reason, you can do it this way:
val newList: MutableList<Int> =
list.fold(ArrayList()) { acc, x -> acc.apply { add(x + 10) } }
Or this way:
val newList: MutableList<Int> =
list.fold(ArrayList()) { acc, x ->
acc += x + 10
acc
}

Loop String from Console and sum odd numbers

fun main(args: Array<String>) {
println("Number: ")
val num = readLine()!!.toInt()
var sum = 0
for (digit in num) {
if (digit % 2 != 0) {
sum += digit
}
}
println("$sum")
}
I need this loop to go through every digit in the number and sums all the digits that are odd.
It gives me an error on num "For-loop range must have an 'iterator()' method"
You cannot iterate over an Int like num:
val num = readLine()!!.toInt()
You can fix it without a loop and by using standard functions map, filter and sum:
val sum = readLine()!!.toCharArray()
.map { it.toString().toInt() }
.filter { it % 2 != 0 }
.sum()
The filter-condition for even numbers would be it % 2 == 0
EDIT
For your homework, do this:
val num = readLine()!!.toCharArray()
var sum = 0
for (a in num) {
val intVal = a.toString().toInt()
if (intVal % 2 != 0) {
sum += intVal
}
}