Why is "contains" or "in" not working in Kotlin - kotlin

I'm currently working on a project with different functions.
One of the functions is supposed to take a map and a string as inputs, then compare the items in the map and string and create a new map with matching items.
This function is not working because "containsKey" always returns false.
"in" always returns fault.
I tried substituting the map with a mutable List and got the same results.
I tried this same code on kotlin playground, link; https://pl.kotl.in/zZm1Smz3Z
and it worked well but wouldn't work on the platform I am using.
The function is below
fun createVariable (stringList: String, mapNumber: MutableMap<String, String>):MutableMap<String, String> {
var mapOfNumber = mutableMapOf<String, String>()
var conList : MutableList<String> = mutableListOf()
var regex = Regex("\\s*=\\s*")
var regez = Regex("\\s+")
var numberList = stringList.split(regex).toMutableList()
for (i in 0.. numberList.size -1) {
if ( regez.matches(numberList[i])) {
numberList.removeAt(i)
}
}
if (mapNumber.containsKey(numberList[1])) {
mapOfNumber.put(numberList[0], (mapNumber[numberList[1]])?: "0")
}
print(mapOfNumber)
return mapOfNumber
}

Related

Mapping array elements using a function in Kotlin

New to Kotlin from Python. In Python, I can simply use the code below to pass each element of a List to a multiline function and return an iterator of the result.
countArr = list(map(countReps, arr))
In Kotlin, I found that I had to do the following. Am I missing something?
fun LetterCountI(str: String): String {
val arr = str.split(" ")
var transform:(String) -> Int = {countReps(it)}
val countArr = arr.map(transform)
val mxIndex:Int
var ans:String
if (countArr.max()!=1){
mxIndex = countArr.indexOf(countArr.max())
ans = arr[mxIndex]
} else {
ans = "-1"
}
return ans;
}
fun countReps(str: String): Int {
var m = mutableMapOf<Char, Int>()
var v:Int
for (c in str){
if (c in m.keys){
v = m[c]?:0
m.put(c,v+1)
} else {
m.put(c,1)
}
}
return m.values.max() ?: 0
}```
I'm having a bit of a hard time understanding your code, but one thing I can tell you is that you can replace
var transform:(String) -> Int = {countReps(it)}
val countArr = arr.map(transform)
with
val countArr = arr.map(::countReps)
In addition to the line you ask about, just about all of that code could be rewritten more concisely and idiomatically in Kotlin. For example:
fun String.wordWithMostRepeatedLetters()
= split(" ")
.associateWith{ it.maxRepeatedLetters() }
.filter{ it.value > 1 }
.maxByOrNull{ it.value }?.key ?: "-1"
fun String.maxRepeatedLetters()
= groupBy{ it }.map{ it.value.size }.maxOrNull() ?: 0
I've renamed the functions to try to explain what they give; replaced the countArr list with a map from each word to its count, so that you don't need to re-scan it to find the word resulting; and changed both functions to take a String receiver instead of a parameter. Then, because each variable was only used once, I removed them and made it all in-line, using an expression body for each function.
Some of those things don't always improve clarity, of course, especially for long functions — but I hope it demonstrates how concise Kotlin can be. (Hopefully without sacrificing maintainability. Which version would be easier to read? Which would be more likely to harbour subtle bugs?)
It's still not clear what the hard-coded "-1" return value indicates, though… If no word has any repeated letters, a null return would be more idiomatic. (Or it would be simpler just to return the first word, removing the filter() call, and returning null only if the string is blank.)

Kotlin combine lists based on common property

I have two functions (GetPodsOne and GetPodsTwo) that return me a big csv string. I then do some processing to discard the part of the string I don't want. See snippet below.
var podValues = execGetPodsOne()
val testPodValuesLst: List<String> = podValues.split(",").map { it -> it.substringAfterLast("/") }
testPodValuesLst.forEach { it ->
 println("value from testPodList=$it")
 }
podValues = execGetPodsTwo()
val sitPodValuesLst: List<String> = podValues.split(",").map { it -> it.substringAfterLast("/") }
sitPodValuesLst.forEach { it ->
 println("value from sitPodList=$it")
 }
This leaves me with two lists. See output of the above below:
value from testPodList=api-car-v1:0.0.118
value from testPodList=api-dog-v1:0.0.11
value from testPodList=api-plane-v1:0.0.36
value from sitPodList=api-car-v1:0.0.119
value from sitPodList=api-dog-v1:0.0.12
value from sitPodList=api-plane-v1:0.0.37
What i would like to do is end up with the objects inside a data class like below:
data class ImageVersions(val apiName: String, val testPodVersion: String, val sitPodVersion: String)
api-car-v1, 0.0.118, 0.0.119
api-dog-v1, 0.0.11, 0.0.12
api-plane-v1, 0.0.36, 0.0.37
I've used test and sit above but I'm going to have maybe another 5 environments eventually. Looking for a nice way to get the versions for each api and easily combine into that ImageVersions data class.
thanks
Considering that you're going to have maybe another 5 environments eventually, I tried to write something that will scale well:
enum class Env { Test, Sit }
data class ImageVersions(val apiName: String, val versions: Map<Env, String?>)
fun String.getNameAndVersion() = substringBefore(':') to substringAfter(':')
fun getVersions(envMap: Map<Env, List<String>>): List<ImageVersions> {
val envApiNameMap = envMap.mapValues { it.value.associate(String::getNameAndVersion) }
val allApiNames = envApiNameMap.flatMap { it.value.keys }.distinct()
return allApiNames.map { apiName ->
ImageVersions(apiName, envApiNameMap.mapValues { it.value[apiName] })
}
}
Playground example
So instead of separate val testPodVersion: String, val sitPodVersion: String, here you have a map. Now the structure of ImageVersions always remains the same irrespective of how many environments you have.
getNameAndVersion is a helper function to extract apiName and version from the original string.
getVersions accepts a list of versions corresponding to each environment and returns a list of ImageVersions
envApiNameMap is same as envMap just that the list is now a map of apiName and its version.
allApiNames contains all the available apiNames from all environments.
Then for every apiName, we take all the versions of that apiName from all the environments.
In future, if your have another environment, just add it in the Env enum and pass an extra map entry in the envMap of getVersions. You need not modify this function every time you have a new environment.
How about this:
val testPodValuesMap = testPodValuesLst.associate { it.split(':').zipWithNext().single() }
val sitPodValuesMap = sitPodValuesLst.associate { it.split(':').zipWithNext().single() }
val mergedMap = (testPodValuesMap.keys + sitPodValuesMap.keys).associateWith { key ->
testPodValuesMap.getValue(key) to sitPodValuesMap.getValue(key)
}
val imageVersions = mergedMap.map { (k, v) -> ImageVersions(k, v.first, v.second) }
println(imageVersions.joinToString("\n"))
which prints
ImageVersions(apiName=api-car-v1, testPodVersion=0.0.118, sitPodVersion=0.0.119)
ImageVersions(apiName=api-dog-v1, testPodVersion=0.0.11, sitPodVersion=0.0.12)
ImageVersions(apiName=api-plane-v1, testPodVersion=0.0.36, sitPodVersion=0.0.37)
As a first step I would extract the apiNames from both lists:
val apiNames = list1.map { it.replace("value from ", "").split("[=:]".toRegex())[1] }
.plus(list2.map { it.replace("value from ", "").split("[=:]".toRegex())[1] })
.distinct()
Then I'd create the ImageVersions instances by looping over apiNames:
val result = apiNames
.map { apiName ->
ImageVersions(
apiName,
(list1.firstOrNull { it.contains(apiName) } ?: "").split(":")[1],
(list2.firstOrNull { it.contains(apiName) } ?: "").split(":")[1]
)
}
.toList()
The reason to first extract the apiNames is, that apiNames missing in one of the two lists will still end up in the final result.
Kotlin Playground

How to merge nested Maps in Kotlin

I'm trying to merge values from two maps in Kotlin, but I can't seem to find a solution online to do so. For example, I have these two Map Objects that have nested maps as shown below.
first: {settings={exit={exitMessage=message0}}, 2=2}
second: {settings={exit={differentMessage=message1, secondMessage=message2}}, 2=zebra}
As you can see, there are some keys that are the same(like settings and exit), but sometimes the keys are different. I'm wondering how I can merge the two Map objects so the end result is like
{settings={exit={exitMessage=message0, differentMessage=message1, secondMessage=message2}}, 2=zebra}
We can't simply "add" the two maps together, because Kotlin will override the Map objects
For example, if we add the two objects up above, as first + second, the end result will just be
{settings={exit={differentMessage=message1, secondMessage=message2}}, 2=zebra}
as the exit map in second takes priority over the exit map object in first.
You can try flattening both maps first:
settings.exit.exitMessage=message0
2=2
settings.exit.differentMessage=message1
settings.exit.secondMessage=message2
2=zebra
Then after merging the maps you end up with this:
settings.exit.exitMessage=message0
settings.exit.differentMessage=message1
settings.exit.secondMessage=message2
2=zebra
And then just rebuild the map again by exploding the keys.
Full code sample here: https://pl.kotl.in/p9BaUVOF3
Flattening a map
fun flatten(source: Map<String, Any>, target: HashMap<String, Any>, prefix: String = "") {
for (entry in source) {
var fullKey = if (prefix.length > 0) prefix + "." + entry.key else entry.key
if (entry.value is Map<*, *>) {
flatten(entry.value as Map<String, Any>, target, fullKey)
continue
}
target.put(fullKey, entry.value)
}
}
Exploding a flattened map
fun explode(source: Map<String, Any>, target: HashMap<String, Any>) {
for (entry in source) {
var targetMap = target
var keySegments = entry.key.split(".")
// Rebuild the map
for (keySegment in keySegments.dropLast(1)) {
if (targetMap.containsKey(keySegment)) {
var value = targetMap.get(keySegment)
if (value is Map<*, *>) {
targetMap = value as HashMap<String, Any>
continue
}
}
var newNestedMap = HashMap<String, Any>()
targetMap.put(keySegment, newNestedMap)
targetMap = newNestedMap
}
// Put values into it
targetMap.put(keySegments.last(), entry.value)
}
}

Сheck if map contains substring. Kotlin

I have a
val map = Map<String,String>
map.put("Nurseiyt","android")
I want to get a value by subString like:
map["Nurs"] should return "android"
is it possible?
Use kotlin.Collections, there are methods like filter.
Two things - it's better to use regular expression. So, you can even get better control what will be returned. And the second one, there can be more than one elements matched to that regex. So that's why I return list.
fun <T> substringKey(map: Map<String, T>, regex: Regex): List<T> {
return map.filter { it.key.contains(regex) }
.map { it.value }
}
If you want to use that notation you need to create your own map and override proper operator. What's worth to notice, you cannot return list of values then. So, in this case I just return first found value.
class SubstringMap<V> : HashMap<String, V>() {
override operator fun get(key: String): V? {
return this.entries.first { it.key.contains(key) }.value
}
}
fun main() {
val map = SubstringMap<String>()
map["Nurseiyt"] = "android"
println(map["Nurs"]) // "android"
}
And as the last thing - in kotlin you can create your own operator, like withKeyPart. This would be much better than overriding default operator (because I wouldn't expect that [] operator will work in different way than usual.
infix fun <V> Map<String, V>.withKeyPart(keyPart: String): List<V> {
return this.filter { it.key.contains(keyPart) }
.map { it.value }
}
and then call it like this:
fun main() {
val map = HashMap<String, String>()
map withKeyPart "KeyPart" // infix notation
map.withKeyPart("KeyPart") // standard call
}
Filtering the map, as per other answers, is simple and straightforward, but it doesn't scale well; it takes time proportional to the size of the map, so if the map could grow big, it could get very slow.
If you're always going to be searching for a leading substring, i.e. the start of a map key, then a better general solution is a data structure called a trie.  This lets you search efficiently, with just one lookup per character.
Of course, writing one from scratch may not be justified for your project.  But there are third-party implementations you could use, such as this one in Apache Commons.  Or see the answers to this question.
write top level function like this
fun HashMap<String, String>.getContainskeyValue(search: String): String?
{
var returnList = ArrayList<String?>()
this.keys.filter { it.contains(search) }.map {
returnList.add(this[it])
}
return returnList.first()
//if you want all keys 'contains' values just return list
/* Ex
map.put("Nurseiyt", "android")
map.put("Nurseiyt1", "androidone")
map.put("Nurseirt2", "andrrroidtwo")
val isContainsdata = map.getContainskeyValue("N")
println(" result " + containsdata)
output :result [andrrroidtwo, android, androidone]
*/
}
then call like this
val map = HashMap<String, String>()
map.put("Nurseiyt", "android")
val containsdata = map.getContainskeyValue("Nurs")
println(" result " + containsdata)
output
android

how to convert a String sentence to arraylist in Kotlin

I have this function to convert string sentence to list words. I created this function in Java and converted to Kotlin using default Kotlin conversion in Android Studio, but I believe there can be many ways to shorten this code in Awesome Kotlin. I will be good if you can share your piece of code and help me(and all) to improve our knowledge in Kotlin.
private fun stringToWords(mnemonic: String): List<String> {
val words = ArrayList<String>()
for (word in mnemonic.trim { it <= ' ' }.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
if (word.isNotEmpty()) {
words.add(word)
}
}
return words
}
I would go for the following:
fun stringToWords(s : String) = s.trim().splitToSequence(' ')
.filter { it.isNotEmpty() } // or: .filter { it.isNotBlank() }
.toList()
Note that you probably want to adjust that filter, e.g. to filter out blank entries too... I put that variant in the comment... (if you use that one, you do not require an initial trim() though)
If you rather want to work with the Sequence you can do so by just omitting the .toList() at the end.
And as also Abdul-Aziz-Niazi said: same is also possible via extension function, if you require it more often:
fun String.toWords() = trim().splitToSequence(' ').filter { it.isNotEmpty() }.toList()
You can do it like this.. Just make a function of return type list.
val s = "This is a sample sentence."
val words:Array<String> = s.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (i in words.indices) {
// You may want to check for a non-word character before blindly
// performing a replacement
// It may also be necessary to adjust the character class
words[i] = words[i].replace("[^\\w]".toRegex(), "")
}
May this will help you :-)
It's easier than you think:
fun stringToWords(mnemonic: String) = mnemonic.replace("\\s+".toRegex(), " ").trim().split(" ")
Remove multiple spaces, trim start and the end, split.
Like an extention:
fun String.toWords() = replace("\\s+".toRegex(), " ").trim().split(" ")
After Roland's suggestion:
fun String.toWords() = trim().split("\\s+".toRegex())
You don't need scopes, the redundant "".toRegex() and the last expression.
You can do something like this:
private fun stringToWords(mnemonic: String): List<String> {
val words = ArrayList<String>()
for (w in mnemonic.trim(' ').split(" ")) {
if (w.isNotEmpty()) {
words.add(w)
}
}
return words
}
Additionally,
If you use this method a lot in this project, you can make it an extension in string class. Paste this method in a separate file(outside a classes or add it in classless .kt file) so it has a global access.
and then you can use it with any string like
myString.toWords() anywhere in the project
The method will look like this
inline fun String.toWords(): List<String> {
val words = ArrayList<String>()
for (w in this.trim(' ').split(" ")) {
if (w.isNotEmpty()) {
words.add(w)
}
}
return words
}