I have the following data class:
data class Foo(val a: Int = 0, val b: Int = 0)
I have a list of Foo's with the following structure:
[ Foo(a = 1), Foo(a = 2), ..., Foo(b = 22), Foo(a = 5), Foo(a = 6), ... ]
(a group of items with a's, then one b, then a's again)
I would like to split above list into three sub-lists like that:
[ Foo(a = 1), Foo(a = 2), ...]
[ Foo(b = 22) ]
[ Foo(a = 5), Foo(a = 6), ...]
sublist of elements that have non-zero a property
list of one element that has non-zero b
remaining sublist of elements that have non-zero a property
Is it possible to achieve using groupBy or partition?
It is not possible to do it via groupBy or partition, because it is not possible to check the past state in those operations. However, you can do it via a fold operation and using mutable lists. Not sure if it fits to your needs but here it goes:
val input = listOf(Foo(a = 1), Foo(a = 2), Foo(b = 22), Foo(a = 5), Foo(a = 6))
val output: List<List<Foo>> = input.fold(mutableListOf<MutableList<Foo>>(mutableListOf())) { acc, foo ->
val lastList = acc.last()
val appendToTheLastList =
lastList.isEmpty() ||
(foo.a != 0 && lastList.last().a != 0) ||
(foo.b != 0 && lastList.last().b != 0)
when {
appendToTheLastList -> lastList.add(foo)
else -> acc.add(mutableListOf(foo))
}
return#fold acc
}
println(output)
outputs:
[[Foo(a=1, b=0), Foo(a=2, b=0)], [Foo(a=0, b=22)], [Foo(a=5, b=0),
Foo(a=6, b=0)]]
Note: I have to point out that this solution is not better than a solution with regular loops.
So you want to 1) ignore the first Foos where a=0, 2) start collecting them when you see Foos where a is non-zero, 3) when you hit a Foo where a=0, put that in another list, because b will be non-zero, 4) start collecting non-zero a's again in a third list?
If that's what you want (this is an extremely specific thing you want and you haven't been clear about it at all) you could do it this way:
data class Foo(val a: Int, val b: Int)
val stuff = listOf(Foo(0,1), Foo(1,2), Foo(3,0), Foo(0, 4), Foo(0, 5), Foo(6, 1), Foo(0,7))
fun main(args: Array<String>) {
fun aIsZero(foo: Foo) = foo.a == 0
// ignore initial zero a's if there are any
with(stuff.dropWhile(::aIsZero)) {
val bIndex = indexOfFirst(::aIsZero)
val listOne = take(bIndex)
val listTwo = listOf(elementAt(bIndex))
val listThree = drop(bIndex+1).filterNot(::aIsZero)
listOf(listOne, listTwo, listThree).forEach(::println)
}
}
You can't use partition or groupBy because your predicate depends on the value of a, but also on whether it happens to represent that one element you want to put in the b list, and for the others whether they appear before or after that b element. Which you don't know before you start processing the list.
You could mess around with indices and stuff, but honestly your use case seems so specific that it's probably better to just do it imperatively instead of trying to cram it into a functional approach.
Related
I want to store a number of counts per value, something like this:
value count
0 -> 6
1 -> 2
2 -> 0
3 -> 7
As shown in the example, the values start at 0 and are consecutive integers.
I want to initialize all counts with 0, so that I can then increment them.
This is what I came up with:
val histogram = Array(numBuckets) { 0 }.toMutableList() as ArrayList
histogram[2]++
It works, but the initialization feels a bit convoluted. Is there a better way? Is the ArrayList the correct collection for the job in the place?
You can just use the MutableList constructor:
val histogram = MutableList(numBuckets) { 0 }
Kevin Coppock's answer works well if the values in the value-count pairs are consecutive and starting at 0. Then the array or list index stands for the value in the value-count pair.
If more flexibility is needed, for example if the values
do not start at zero,
have a step which is not 1,
or have irregular steps (e.g. logarithmic),
it might make sense to introduce pairs, either as Pair<Int, Int> or in the form of a data class:
import kotlin.math.pow
data class HistogramEntry(
var value: Int,
var count: Int
)
Example:
val numBuckets = 5
val regularHistogram = List(numBuckets) { HistogramEntry(it, 0) }
regularHistogram[2].count++
regularHistogram(::println)
Output:
HistogramEntry(value=0, count=0)
HistogramEntry(value=1, count=0)
HistogramEntry(value=2, count=1)
HistogramEntry(value=3, count=0)
HistogramEntry(value=4, count=0)
Another example:
val numBuckets = 5
val logarithmicHistogram = List(numBuckets) { HistogramEntry(10f.pow(it + 1).toInt(), 0) }
logarithmicHistogram[2].count = 12345
logarithmicHistogram.forEach(::println)
Output:
HistogramEntry(value=10, count=0)
HistogramEntry(value=100, count=0)
HistogramEntry(value=1000, count=12345)
HistogramEntry(value=10000, count=0)
HistogramEntry(value=100000, count=0)
And of course a HistogramEntry list could also be built manually:
val list = listOf(
HistogramEntry(value = 234, count = 0),
HistogramEntry(value = 36, count = 0),
HistogramEntry(value = 9, count = 0),
HistogramEntry(value = 178, count = 0),
HistogramEntry(value = 11, count = 0)
)
If I have the below two maps:
val x = mapOf("a" to 10, "b" to 5)
val y = mapOf("a" to 4)
val result = //
How can I get the result to be: {a=4, b=5}? I want the value to be overwritten if the key exists.
val result = x + y does exactly what you need, the second parameter (y) will overwrite the values with the same keys from the first parameter (x)
val result = x.toMutableMap().apply { putAll(y) }
Assuming we are given a list of integers R = [3,5,3,6,0,6,7], an threshold x (integer) and a window size (integer) p. For example, x=4 and p = 2.
I need to find the first index t that verifies the the following conditions:
R[t] >= 4, R[t+1] >= 4. Since p=2, we need to only verify for two boxes t and t+1. If p was equal to 3 we will need to verify for t, t+1 and t+2.
Here the t I am looking for is 5 (indexing is starting from 0).
How to write this in a elegant way in Kotlin (rather than looping on the elements).
A tentative that is giving an error (x=4 and p = 2. The output should be 3 since we start indexing by 0):
val numbers = listOf(1, 2, 3, 4, 6, 8, 2)
val firstIndex = numbers.find { it >= 4 for it in it..it+2-1}
val numbers = listOf(1, 2, 3, 4, 6, 8, 2)
val p = 2
val x = 4
val t = numbers.windowed(p).indexOfFirst { window -> window.all { it >= x } } // t == 3
t will be equal to -1 in case if no matches will be found
Use windowed to check groups of values for each index in the list. Use withIndex() so you are iterating with the indices, which you need in your final result. Then use firstOrNull() (which find() is a redundant alias of). And finally, take ?.index to get the index of the first entry that satisfies the condition, or null if none satisfy.
val x = 4
val p = 3
val list = listOf(2,5,3,6,0,6,7)
val t = list
.windowed(p)
.withIndex()
.firstOrNull { (_, sublist) -> sublist.all { it >= x } }
?.index
find Returns the first element matching the given predicate, or null if no such element was found.
If I've understood correctly, this should work:
fun main() {
val list = listOf(3,5,3,6,0,6,7)
val p = 2
val x = 4
val t = list.withIndex().windowed(p).firstOrNull() { window ->
window.all { it.value >= x }
}?.first()?.index
println(t)
}
Output:
5
Is there any function (like fold, map, filter), which gets 2 arrays and lambda-function (for example multiplication) as parameters and returns third array?
I've used cycle for, but is there more beautiful method?
Yes, there is zip (nice example at the bottom of the page), see this (different) example:
fun main() {
val a = arrayOf( 1, 2, 3, 4 )
val b = arrayOf( 1, 2, 3, 4 )
val c = a.zip(b) { i, j -> i * j }
println(c)
}
which outputs
[1, 4, 9, 16]
There isn't a built in specifically but you can do this:
array1.zip(array2).map { (x,y) -> x*y }
I have a list as below
{("a", 1), ("b", 2), ("c", 3), ("a", 4)}
I want to convert it to a map of list as below
{("a" (1, 4)), ("b", (2)), ("c", (3)))}
i.e. for a, we have a list of 1 and 4, since the key is the same.
The answer in
How to convert List to Map in Kotlin? only show unique value (instead of duplicate one like mine).
I tried associateBy in Kotlin
data class Combine(val alpha: String, val num: Int)
val list = arrayListOf(Combine("a", 1), Combine("b", 2), Combine("c", 3), Combine("a", 4))
val mapOfList = list.associateBy ( {it.alpha}, {it.num} )
println(mapOfList)
But doesn't seems to work. How could I do it in Kotlin?
Code
fun main(args: Array<String>) {
data class Combine(val alpha: String, val num: Int)
val list = arrayListOf(Combine("a", 1), Combine("b", 2), Combine("c", 3), Combine("a", 4))
val mapOfList = list.associateBy ( {it.alpha}, {it.num} )
println(mapOfList)
val changed = list
.groupBy ({ it.alpha }, {it.num})
println(changed)
}
Output
{a=4, b=2, c=3}
{a=[1, 4], b=[2], c=[3]}
How it works
First it takes the list
It groups the Combines by their alpha value to their num values
You may group the list by alpha first and then map the value to List<Int>:
data class Combine(val alpha: String, val num: Int)
val list = arrayListOf(Combine("a", 1), Combine("b", 2), Combine("c", 3), Combine("a", 4))
val mapOfList = list
.groupBy { it.alpha }
.mapValues { it.value.map { it.num } }
println(mapOfList)
Here's a slightly more concise version of Jacky Choi's solution.
It combines the grouping and the transforming into one call to groupBy().
val mapOfList = list
.groupBy (
keySelector = { it.name },
valueTransform = { it.num },
)