In Kotlin is there function or a way to also have an index when using the all extension?
With this kind of situation:
val givenKeys = arrayOf("SHIFT", "BUILDING", "FLOOR")
val givenVals = arrayOf("NIGHT", "ALPHA", "THIRD")
val successfulMatch = mapOf(
Pair("SHIFT", "NIGHT"), Pair("BUILDING", "ALPHA"), Pair("FLOOR", "THIRD")
)
val unsuccessfulMatch = mapOf(
Pair("SHIFT", "NIGHT"), Pair("BUILDING", "BETA"), Pair("FLOOR", "FIRST")
)
fun isMatch(candidate: Map<String, String>, keys: Array<String>, vals: Array<String>): Boolean {
var matches = true
keys.forEachIndexed { i, key ->
if(!candidate.containsKey(key) || vals[i] != candidate[key]) {
matches = false
}
}
return matches
}
isMatch(successfulMatch, givenKeys, givenVals) // returns true
isMatch(unsuccessfulMatch, givenKeys, givenVals) // returns false
I want to do something like this
fun isMatch(candidate: Map<String, String>, keys: Array<String>, vals: Array<String>): Boolean {
return keys.allIndex {i, key ->
candidate.containsKey(key) && vals.any {it == candidate[key]}
}
}
Is there any function like that?
You can use withIndex:
return keys.withIndex().all { (i, key) ->
//...
}
Note that it creates an Iterable<IndexedValue>, so you would typically use the dereference operator ( ) for the lambda parameter.
Related
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)
I'm looking for a Kotlin way to do a dynamic values substitution into a string.
It is clear how to implement it, just want to check if there is something similar in standard library.
Could you help me to find a function which given template and data map returns a resulting string with all template keys replaced with their values?
fun format(template: String, data: Map<String, Any>): String { /* magic */ }
format("${a} ${b} ${a}", mapOf("a" to "Home", "b" to "Sweet)) // -> "Home Sweet Home"
fun format(template: String, data: Map<String, String>): String {
var retval = template
data.forEach { dataEntry ->
retval = retval.replace("\${" + dataEntry.key + "}", dataEntry.value)
}
return retval
}
// The $ signs in the template string need to be escaped to prevent
// string interpolation
format("\${a} \${b} \${a}", mapOf("a" to "Home", "b" to "Sweet"))
Not shorter than lukas.j's answer, just different (using Regex):
val regex = "\\\$\\{([a-z])}".toRegex()
fun format(template: String, data: Map<String, String>) =
regex.findAll(template).fold(template) { result, matchResult ->
val (match, key) = matchResult.groupValues
result.replace(match, data[key] ?: match)
}
I did not find any thing standard to solve the problem.
So here is a balanced (readability/performance/extensibility) solution also handling cases when some substitutions are undefined in dataMap.
makeString("\${a} # \${b} # \${c}", mapOf("a" to 123, "c" to "xyz")) // => "123 # ??? # xyz"
--
object Substitutions {
private val pattern = Pattern.compile("\\$\\{([^}]+)\\}")
fun makeString(
template: String,
dataMap: Map<String, Any?>,
undefinedStub: String = "???"
): String {
val replacer = createReplacer(dataMap, undefinedStub)
val messageParts = splitWithDelimiters(template, pattern, replacer)
return messageParts.joinToString("")
}
private fun createReplacer(dataMap: Map<String, Any?>, stub: String): (Matcher) -> String {
return { m ->
val key = m.group(1)
(dataMap[key] ?: stub).toString()
}
}
private fun splitWithDelimiters(
text: String,
pattern: Pattern,
matchTransform: (Matcher) -> String
): List<String> {
var lastMatch = 0
val items = mutableListOf<String>()
val m = pattern.matcher(text)
while (m.find()) {
items.add(text.substring(lastMatch, m.start()))
items.add(matchTransform(m))
lastMatch = m.end()
}
items.add(text.substring(lastMatch))
return items
}
}
I am playing with Kotlin and I am trying to convert a working Scala code to Kotlin. Everything seems to go pretty well but the compiler gives me this error and I dont know how to handle it.
Type mismatch: inferred type is Any but ExQuestion was expected for this line: return makeMap(questions, add2)
I am using a generic function because I need to access members of type A when building the map and the members would be visible through the lambda function provided.
Here's the code which you can copy into the Kotlin sandbox:
data class ExQuestion(val area: String, val rId: String, val text: String, val rIdAnswer: String, val line: Long)
fun main() {
fun <A> makeMap(list: List<A>, addValue: (A, MutableMap<String, A>) -> Unit): Map<String, A> {
val map = mutableMapOf<String, A>()
for( item in list) {
addValue(item, map)
}
return map
}
val add2: (ExQuestion, MutableMap<String, ExQuestion>) -> Unit =
{ question: ExQuestion, map: MutableMap<String, ExQuestion> ->
val key = question.rId
if (map[key] == null) {
map[key] = question
} else {
println("Id Frage mehrfach vorhanden - " + key)
}
}
val questions = listOf(ExQuestion("Area", "Q01", "text", "A01",1))
return makeMap(questions, add2)
}
Working code:
data class ExQuestion(val area: String, val rId: String, val text: String, val rIdAnswer: String, val line: Long)
fun main() {
fun <A> makeMap(list: List<A>, addValue: (A, MutableMap<String, A>) -> Unit): Map<String, A> {
val map = mutableMapOf<String, A>()
for( item in list) {
addValue(item, map)
}
return map
}
val add2: (ExQuestion, MutableMap<String, ExQuestion>) -> Unit =
{ question: ExQuestion, map: MutableMap<String, ExQuestion> ->
val key = question.rId
if (map[key] == null) {
map[key] = question
} else {
println("Id Frage mehrfach vorhanden - " + key)
}
}
val questions = listOf(ExQuestion("Area", "Q01", "text", "A01",1))
val map = makeMap(questions, add2)
println(map.values)
}
I'm not sure what your question is, but you can convert your list of questions to a map keyed on rId by doing:
val map = questions.map { it.rId to it }.toMap()
println(map)
Result:
{Q01=ExQuestion(area=Area, rId=Q01, text=text, rIdAnswer=A01, line=1)}
Update in response to comments.
You can achieve that without a mutable map by doing something like this:
val map = questions
.groupBy { it.rId }
.mapValues { (key, values) ->
if (values.size > 1) println("Id Frage mehrfach vorhanden - $key")
values.first()
}
However, I think your mutable map solution is fine and arguably clearer, so this is just for demonstration.
I read carefully the ArrayList documentation in Kotlin and apparently there is no way to search a value in ArrayList starting from a pointer. The alternative is write your own function iterating the right elements in ArrayList and testing the condition.
So I've programmed the following code:
fun <T> ArrayList<T>.findNext(cond: (T) -> Boolean, p: Int = 0): Int {
for (i in p..this.lastIndex)
if (cond(this[i])) return i
return -1
}
data class Person (
var name: String,
var age: Int
)
fun main() {
var v = arrayListOf<Person>()
v.add(Person("Paul", 22))
v.add(Person("Laura", 24))
v.add(Person("Paul", 50))
v.add(Person("Mary", 24))
println(v.findNext({it.name=="Paul"})) // 0
println(v.findNext({it.name=="Paul"}, 1)) // 2
println(v.findNext({it.name=="Paul"}, 3)) // -1
}
Is there something better than this?
You can avoid any intermediate collections:
inline fun <T> List<T>.findNext(p: Int = 0, cond: (T) -> Boolean) =
listIterator(p).withIndex().asSequence().find { cond(it.value) }?.let { it.index + p }
By swapping the arguments you can call it like this:
println(v.findNext {it.name=="Paul"}) // 0
println(v.findNext(1) {it.name=="Paul"}) // 2
println(v.findNext(3) {it.name=="Paul"}) // null
fun main() {
var v = arrayListOf<Person>()
v.add(Person("Paul", 22))
v.add(Person("Laura", 24))
v.add(Person("Paul", 50))
v.add(Person("Mary", 24))
println(v.findNext({ it.name == "Paul" },0))//IndexedValue(index=0, value=Person(name=Paul, age=22))
println(v.findNext({ it.name == "Paul" },2))//IndexedValue(index=2, value=Person(name=Paul, age=50))
println(v.findNext({ it.name == "Paul" },3))//null
}
private fun <T> List<T>.findNext(cond: (T) -> Boolean, position: Int): IndexedValue<T>? {
return withIndex().filter { it.index >= position }.firstOrNull { cond(it.value) }
}
maybe use withIndex and a filter ?
val arrayNames = listOf<String>("Paul", "Ann", "Paul", "Roger","Peter")
arrayNames.withIndex().filter {
it.value == "Paul" //value contains the original name
}.forEach{
println(it.index) //indext contains the position.
}
this will give you the output 0 and 2
for your case (person object instead of String) you will use
it.value.name == "Paul"
var chart_values: MutableSet<MutableMap.MutableEntry<String, Any>>? = mutableSetOf()
Printing chart_values:
[ground={},
ground_level={0=115, 1=4, 2=0, 3=37, 4=63, 5=44, 6=40, 7=9},
ground_over={0=3, 1=3, 2=3, 3=3, 4=3, 5=3, 6=3}
date_of_birth=1988-07-18T00:00Z]
I would like to remove ground={} from the chart_values
Given that chartValues is of type MutableSet<MutableMap.MutableEntry<String, Any>>? you can do the following to remove any entry with an empty map as a value:
chartValues?.removeAll { (_, value) ->
(value as? Map<*, *>)?.isEmpty() == true
}
as? is called safe cast operator and will return the casted object or null if the cast did not succeed.
Note:
You might be better off using a MutableMap<String, Any>
use val instead of var, since you want to mutate the collection an not the reference to it
BTW:
Your set can be actually a map Map<String, Any>
chart_values doesnt comply with kotlins naming convetions. Use camel case
Since the value is of type Any - which should be changes to more specific type if possible - we have to check for instance first
val chart_values: MutableSet<MutableMap.MutableEntry<String, Any>>? = mutableSetOf()
val withoutEmptyValues = chart_values.filter { (_, value) -> value is Collection<*> && value.isNotEmpty() }
EDIT
If some elements are no collections:
val withoutEmptyValues = chart_values.filter { (_, value) -> if(value is Collection<*>) value.isNotEmpty() else true }
Test
I can't create instances of MutableMap.MutableEntry, so I created a mao which does it for me internally:
val map: MutableMap<String, Any> = mutableMapOf(
"ground" to listOf<Int>(1, 2, 3),
"empty" to emptyList<Double>(),
"date" to "1988-07-18T00:00Z"
)
val withoutEmptyValues = map
.filter { (_, value) -> if (value is Collection<*>) value.isNotEmpty() else true }
assertThat(withoutEmptyValues).isNotEmpty.isEqualTo(
mutableMapOf<String, Any>(
"ground" to listOf<Int>(1, 2, 3),
"date" to "1988-07-18T00:00Z"
)
)
fun removeMapEntryIfEmpty() {
val iterator: MutableIterator<MutableMap.MutableEntry<String, Any>> =
player.chart_values?.iterator()!!
iterator.forEach {
// it: MutableMap.MutableEntry<String, Any>
if (it.value.toString() == "{}") {
iterator.remove()
}
}
}