Count the number of a - kotlin

I have this code :
fun main(args:Array<String>){
var a = "banana, eat, one"
var a1 = a1.split("a").toMutableList()
a1.sortBy { it.toCharArray().count { it == 'a' } }
}
This code allows me to sort my list a1 by number of a but I would like to have for each words (banana, eat, one) the number of a. I thought to create a HashMap something like this :
(banana,3)
(eat,1)
(one,0)
But I don't know if there is a function which allows me to do this ?
Could you help me ?
Thank you.

Your existing code didn't compile, but I assume you meant to use a in the second line and split on ,. If so, you can use associateWith to create a Map where the keys are your words, and the values are the counts of 'a' in each:
val a = "banana, eat, one"
val a1 = a.split(", ").toMutableList()
val a2 = a1.associateWith { word -> word.count { char -> char == 'a' } }
println(a2) // {banana=3, eat=1, one=0}

fun main(args:Array<String>){
val input = "banana, eat, one"
// split the words
val split = input.split(",")
val map = HashMap<String, Int>()
for (word in split) {
var countA = 0
// count the character 'a' per word
for (character in word) {
if (character == 'a' || character == 'A') {
countA++
}
}
map[word] = countA
}
println(map)
}

"banana, eat, one".split(",").associateBy({ it }, { it.count { it.toLowerCase() == 'a' } })

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)

How to split operators and operands into two arrays?

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

Find any 2 objects in a list matching criteria in one iteration

I have a list of custom objects. I would like to retrieve any 2 that meet 2 different conditions.
E.g.
Assume
class Person {
val age: Int
//etc
}
And I have a List<Person>. I would like to get 2 Person that have age == 21 or age == 31
I know I can do a loop for that and break using some flag once I find both, but I was wondering if there was some more Kotlin idiomatic way for this instead of:
var p1: Person?
var p2: Person?
list.forEach{
if(it.age == 21) {
p1 = it
}
else if(it.age == 31) {
p2 = it
}
if(p1 != null && p2 != null) break
}
I can only think of 2 possible solutions:
An inefficient but simple way (2 partial iterations)
val list = listOf(Person(21), Person(23), Person(31), Person(20))
var p1: Person? = list.find{ it.age == 21 }
var p2: Person? = list.find{ it.age == 31 }
This will iterate twice, but will stop the iteration as soon as a result is found.
Based on your own solution, only iterates once
val list = listOf(Person(21), Person(23), Person(31), Person(20))
var p1: Person? = null
var p2: Person? = null
//forEach is a method, not an actual loop, you can't break out of it, this is a bypass (you can also enclose it into a fun and return from there to act as a break)
run loop#{
list.forEach {
when {
p1 == null && it.age == 21 -> p1 = it
p2 == null && it.age == 31 -> p2 = it
}
if (p1 != null && p2 != null) return#loop
}
}
The p1 == null check will make is so that we don't reasign the same value multiple times, thus getting the first result. It is better even if we aren't necessarily looking for the first result, since it won't make multiple assignments.
Ps: both solutions will leave you with nullable p1 and p2, you must remember to deal with that accordingly afterwards.
No, you are trying to do quite a specific operation, so I don't think there is a built-in function and you will need to implement it yourself. Since you want to only iterate the list once, I think it would be best to:
Implement it with traditional list iteration
Wrap it up in a generic extension function for idiomatic Kotlin use
data class Person(val age: Int)
fun <T> Iterable<T>.findFirstTwo(
predicateA: (T) -> Boolean,
predicateB: (T) -> Boolean
): Pair<T, T> {
var first: T? = null
var second: T? = null
val iterator = iterator()
var remainingPredicate: (T) -> Boolean = { false }
while (first == null && iterator.hasNext()) {
val item = iterator.next()
when {
predicateA(item) -> {
first = item
remainingPredicate = predicateB
}
predicateB(item) -> {
first = item
remainingPredicate = predicateA
}
}
}
while (second == null && iterator.hasNext()) {
val item = iterator.next()
if (remainingPredicate(item)) {
second = item
}
}
require(first != null && second != null) { "Input does not satisfy predicates" }
return Pair(first, second)
}
fun main() {
val people = (1..20).map { Person(it) }
val (seven, twelve) = people.findFirstTwo({ it.age == 12 }, { it.age == 7 })
val (one, nineteen) = people.findFirstTwo({ it.age == 1 }, { it.age == 19 })
val error = try {
people.findFirstTwo({ it.age == 100 }, { it.age == 3 })
} catch (e: Exception) {
e.message
}
println(one)
println(seven)
println(twelve)
println(nineteen)
print(error)
}
Output:
Person(age=1)
Person(age=7)
Person(age=12)
Person(age=19)
Input does not satisfy predicates
As a lot of people mentioned that find will probably be the best solution for this kind of problem but we will have to use it twice, once for 21 and other time for 31.
even though find will return a single object: <T> Iterable<T>.find(predicate: (T) -> Boolean): T?
it doesn't mean we can't find another one in the same iteration.
for example:
var ageParam = -1
var p1: Person? = null
var p2: Person? = list.find { person ->
p1?.let {
//p1 - the first person found, now we will iterate until age == ageParam(21/31)
person.age == ageParam
} ?: run {
//p1 - the first person hasn't found yet.
if (person.age == 21) {
p1 = person // saving person
ageParam = 31 // setting next condition to test
} else if (person.age == 31) {
p1 = person
ageParam = 21
}
false // it's false in order to keep iterate after P1 found
}
}
Your solution is close to optimal. But it doesn't take first objects meeting criteria. For listOf(Person(id=1, age=21), Person(id=2, age=21), Person(id=3, age=31)) it will return p1 = Person(id=2, age=21).
To fix this you need to have additional comparsions with null in your conditional expressions (after implementing this, your second if-statement could be merged into branches of the first one to avoid repeating checks). Also, you can't use break inside forEach - it should be substituted with simple loop.
All together:
var p1: Person? = null
var p2: Person? = null
for (it in list) {
if (p1 == null && it.age == 21) {
p1 = it
if (p2 != null) break
} else if (p2 == null && it.age == 31) {
p2 = it
if (p1 != null) break
}
}
To generalize this you may create extension function:
fun <T> Iterable<T>.takeFirstTwo(predicate1: (T) -> Boolean, predicate2: (T) -> Boolean): Pair<T?, T?> {
var p1: T? = null
var p2: T? = null
for (it in this) {
if (p1 == null && predicate1(it)) {
p1 = it
if (p2 != null) break
} else if (p2 == null && predicate2(it)) {
p2 = it
if (p1 != null) break
}
}
return p1 to p2
}
//Usage:
val is21 = { it: Person -> it.age == 21}
val is31 = { it: Person -> it.age == 31}
val (p1, p2) = list.takeFirstTwo(is21, is31)
You can use the filter keyword to filter any collection
val matches = list.filter { if (it.age == 21 || it.age == 31) }
matches will be a list where all Person objects have an age of 21 or 31.
I do not think there is such a method in List, the closest statement would be get first match using list.first{it.age == 21 || it.age == 31 }, which will get the first item matching given predicate then breaks the loop, may be you can write your own extension to filter first n numbers
fun <T> Iterable<T>.firstN(n: Int, predicate: (T) -> Boolean): List<T> {
val output = ArrayList<T>()
var count = 0
for (element in this){
if(count == n) break
if (predicate(element)){
count++
output.add(element)
}
}
return output
}
You can do below to get first two elements
with(list.firstN(2){ it.age == 21 || it.age == 31}){
if(size == 2){
val (p1, p2) = this
}
}

How to change the characters in a string in kotlin

I am trying to change the character in a string to some other character.
Here is my code
fun main(args: Array<String>) {
var str: String = "H...H"
for(i in 0..str.length-1) {
if( str[i] == '.')
str[i] = 'B'
}
println(ans)
}
But this produces the error:
jdoodle.kt:20:16: error: no set method providing array access
str[i] = 'B'
But the following code works fine:
fun main(args: Array<String>) {
var str: String = "H...H"
var ans : String = ""
for(i in 0..str.length-1) {
if( str[i] == 'H')
ans += str[i]
else if( str[i] == '.')
ans += 'B'
}
println(ans)
}
I just want to change all the ..... in the string to B.
Like "H...H" to "HBBBH"
Why is the first code not working?
The first example does not work because Strings in kotlin are immutable and you cannot change characters. Instead, you have to create a new String, like your second example (which, in fact, creates a new String for each time through the loop).
Kotlin has a replace function for you:
fun main() {
val input = "H...H"
val output = input.replace('.', 'B')
println(output) // Prints "HBBBH"
}

How can I remove the char 'a' from a list?

I have this code :
fun main(args:Array<String>){
var a = "eat, banana, one"
var a1 = a.split(",").toMutableList()
a1.sortBy { it.toCharArray().count { it == 'a' } }
var a2 = a1.associateWith { word -> word.count { char -> char == 'a' } }
a2.keys.filterNot { c -> "a".contains(c)}
}
Actually, I want to remove the "a" in the word that I have using this line :
a2.keys.filterNot { c -> "a".contains(c)} but it does not work.
How could I do to remove all the a in a2 ?
Thank you very much !
In order to remove all a characters from your keys, you can replace them with an empty string:
a2.mapKeys { it.key.replace("a", "")}
you can map the keys to a new map and replace the a with an empty String in the keys. You then need to use the new created map as result:
fun main(args:Array<String>){
val a = "eat, banana, one"
val a1 = a.split(",").toMutableList()
a1.sortBy { it.toCharArray().count { it == 'a' } }
val a2 = a1.associateWith { word -> word.count { char -> char == 'a' } }
val result = a2.mapKeys { it.key.replace("a", "")}
println(result) // prints { one=0, et=1, bnn=3}
}