How to split operators and operands into two arrays? - kotlin

I want to split this content into two seperate arrays numbers and operators in Kotlin.
The content is "2*5-6+7".
This is my code which doesnt work:
val arrnum = content.split("[-+*/]").toTypedArray()
val operator = content.split("[0123456789]").toTypedArray()

Maybe something like this, using partition:
val OPERATORS = setOf('*', '+', '-', '/')
fun main() {
val (operators, numbers) = "2*5-6+7".toList().partition { it in OPERATORS }
println(operators)
println(numbers)
}
Prints:
[2, 5, 6, 7]
[*, -, +]

If you want to use regular expressions you have to wrap them into Regex("[-+*/]")
The full example would look like this:
val arrnum = content.split(Regex("[-+*/]")).toTypedArray()
val op = content.split(Regex("[0123456789]")).toTypedArray()
Other things to note: "operator" is a keyword in Kotlin. You can simplify [0123456789] to [\d] (with double \ in Kotlin). The operator array will contain empty entries for what's left of of the two and right of the 7. You can filter them out like .filter { it.isNotBlank() }

Solution without regular expressions, but with mutability:
const val OPERATORS = "*+-/"
fun main() {
val s = "2*55-6+7"
val numbers = mutableListOf<Int>()
val operators = mutableListOf<Char>()
var prevIndex = 0
s.withIndex().forEach { (index, c) ->
if (c in OPERATORS) {
operators.add(c)
numbers.add(s.substring(prevIndex until index).toInt())
prevIndex = index + 1
}
}
numbers.add(s.substring(prevIndex).toInt())
println(operators)
println(numbers)
}
Solution without regular expressions in functional style:
const val OPERATORS = "*+-/"
fun main() {
val s = "2*55-6+7"
val operatorsIndexed = s.withIndex().filter { it.value in OPERATORS }
val operators = operatorsIndexed.map { it.value }
val numbers = listOf(-1, *operatorsIndexed.map { it.index }.toTypedArray(), s.length)
.windowed(2)
.map { (from, to) -> s.substring(from + 1 until to).toInt() }
println(operators)
println(numbers)
}

Related

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)
}

incrementing hash map count in Kotlin

I have the function below. However, when I pass a string to it, I get the following error:
error: operator call corresponds to a dot-qualified call 'charCountMap.get(c).plus(1)' which is not allowed on a nullable receiver 'charCountMap.get(c)'. charCountMap.put(c, charCountMap.get(c) + 1)
private fun characterCount(inputString:String) {
val charCountMap = HashMap<Char, Int>()
val strArray = inputString.toCharArray()
for (c in strArray)
{
if (charCountMap.containsKey(c))
{
charCountMap.put(c, charCountMap.get(c) + 1)
}
else
{
charCountMap.put(c, 1)
}
}
}
The Kotlin Standard Library has groupingBy and eachCount for this purpose, you don't need to do any of this manually:
private fun characterCount(inputString:String) {
val charCountMap : Map<Char, Int> = inputString.groupingBy { it }.eachCount()
}
Note that I put the type on charCountMap for clarity, but it can be left off and inferred.
There is nice compute method in HashMap for this:
private fun characterCount(inputString:String) = hashMapOf<Char, Int>().also { charCountMap ->
inputString.forEach { charCountMap.compute(it) { _, v -> if (v == null) 1 else v + 1 } }
}
Both the other answers are correct. Todd's answer is right, you don't need to write a function for this. Just use the standard library. And if you are going to write a function that updates maps, Михаил Нафталь's suggestion to use compute() to handle updating existing values is also good.
However, if you're just doing this an an exercise, here are three suggestions to fix/improve your algorithm:
Instead of get(), use getValue(), which does not return null. It will raise an exception if the element does not exist, but you already checked for that.
Use the [] operator instead of put() (no need to, it's just nicer syntax).
You don't need to call toCharArray() because Strings are already iterable.
if (charCountMap.containsKey(c))
{
charCountMap[c] = charCountMap.getValue(c) + 1
}
else
{
charCountMap[c] = 1
}
Rewriting the whole thing using standard formatting:
fun characterCount(inputString: String): Map<Char, Int> {
val charCountMap = mutableMapOf<Char, Int>()
for (c in inputString) {
if (charCountMap.containsKey(c)) {
charCountMap[c] = charCountMap.getValue(c) + 1
} else {
charCountMap[c] = 1
}
}
return charCountMap
}

How to declare mutableListOf of arrays?

I want to declare mutableListOf arrays but I don't know how. Google shows me examples like var mutableList1 = mutableListOf<Int>() and etc, but not arrays case(((
import java.util.*
fun main(args: Array<String>) {
var mutableList1 = mutableListOf<Arrays>()
var mutableList2 = mutableListOf(arrayOf<Int>()) //works, but it contains empty array:(
mutableList1.add(arrayOf(1,1)) //error
}
You can do it like this:
val list = mutableListOf<Array<Int>>()
list.add(arrayOf(1, 1))
Edit: As Animesh Sahu (thanks!) has pointed out in the comments, if you don't need boxed integers (no nulls in the arrays), you can use the primitive arrays instead and avoid their overhead:
val list = mutableListOf<IntArray>()
list.add(intArrayOf(1, 1))
You don't need to use mutableListOf
for example
val distributionList = mutableListOf<ParticipantDTO>()
participantVolumeList.forEach {
distributionList.add(
ParticipantDTO(
participantUuid = it.get(PARTICIPANT_VOLUMES.PARTICIPANT_UUID)
)
)
}
better rewrite to
val distributionList = participantVolumeList.map { mapToParticipant(it) }
private fun mapToParticipant(
participantVolumesRec: JParticipantRecord
): ParticipantDTO {
return ParticipantDTO().apply {
participantUuid = participantVolumesRec.get(PARTICIPANT_VOLUMES.PARTICIPANT_UUID)
}
}

Split a list into groups based on index of elements

I have a collection (a stream), and want to make it a stream of even and odd index elements Ex.
"slow" -> "solw" or "lwso"
fun part2(s: String) = s
.withIndex()
.groupBy { it.index % 2 }.values
.flatMap { it.map { v -> v.value } }
I learned withIndex lately, of course i could use mapIndexed. But no matter what I do, i need last step of v.value sort of. I wonder if there's any other way of writing simple things like this in kotlin.
You can replace n with 2
fun part(s: String, n: Int): String = s
.withIndex()
.groupBy(keySelector = { it.index % n }, valueTransform = { it.value })
.flatMap { it.value }
.joinToString(separator = "")

How to converter list of tuples to tuple of lists?

I have the example to show what I mean:
fun makeRange(i: Int) = Pair(i - 1, i + 1)
val listOfData = listOf(1, 2, 3, 4, 5, 6)
val pairs = listOfData
.map { makeRange(it) }
val leftRange = pairs.map { it.first }
val rightRange = pairs.map { it.second }
I have some list and function which returns a tuple. But the result I need is touple of two lists. I need something like that:
// can I get something like that ?
val (leftRange, rightRange) = listOfData.map { makeRange(it) } ...
Is there a way to do it?
If you really want to destructure it like this, I would also split up your makeRange-function, e.g.:
fun makeLeftRange(i: Int) = i - 1
fun makeRightRange(i: Int) = i + 1
fun makeRange(i: Int) = makeLeftRange(i) to makeRightRange(i) // if you still need it...
Then you can destructure as follows:
val (leftRange, rightRange) = listOfData.map(::makeLeftRange) to listOfData.map(::makeRightRange)
Or if it is really just such an easy function, why not just use the following instead:
val (leftRange, rightRange) = listOfData.map(Int::dec) to listOfData.map(Int::inc)
// or
val (leftRange, rightRange) = listOfData.map { it - 1 } to listOfData.map { it + 1 }
If you want to keep your makeRange as is and want to do it that way, it will get a bit uglier, e.g.:
val (leftRange, rightRange) = listOfData.map(::makeRange).let {
listOfPairs -> listOfPairs.map { it.first } to listOfPairs.map { it.second }
}
Basically reusing what you've shown in an additional let-statement.
Seems like kotlin unzip function is just what you're looking for.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/unzip.html
In your example the usage would look something like
val (leftRange, rightRange) = pairs.unzip()