Сheck if map contains substring. Kotlin - 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

Related

How can I rewrite this Kotlin filter method more succinctly?

I have a list of strings. I want to search given a query string and say I only want the first 10 matching strings. Here is what I tried, the first one does exactly what I want.
I was wondering if there is a succinct way of writing it.
fun search(query: String): ArrayList<String> {
val found = ArrayList<String>()
for (i in terms.indices) {
if (terms[i].contains(query)) {
found.add(terms[i])
}
if (found.size == 10) {
break
}
}
return found
}
I wanted to use this one but as you can see this is not as efficient as the above one because it goes through the whole list
fun search2(query: String): List<String> {
return terms.filter { it.contains(query) }.take(10)
}
Using a sequence should give you what you want:
fun search2(query: String): List<String> {
return terms.asSequence().filter { it.contains(query) }.take(10).toList()
}
You might not need the toList if you're fine with keeping a sequence as result.

Map values of JsonNode arrays in Kotlin

I have two JsonNode objects which are arrays returned from API calls and look like this
exportedNodeArray:
[
{"key":"111", "value":"aaa"},
{"key":"222", "value":"bbb"},
{"key":"333", "value":"ccc"}
]
localNodeArray
[
{"key":"999", "value":"aaa"},
{"key":"888", "value":"bbb"},
{"key":"777", "value":"ccc"}
]
The required output is a Map of any keys which correspond to the same values in each array. The values are guaranteed to be unique within an array.
"111"="999"
"222"="888"
"333"="777"
This function returns the correct result, but seems like a very in-elegant way to do it.
fun mapIds(exportedNodeArray: JsonNode, localNodeArray: JsonNode) : MutableMap<String, String?> {
val localMap = mutableMapOf<String, String>()
localNodeArray.forEach {
localMap[it["value"].asText()] = it["key"].asText()
}
val idMap = mutableMapOf<String, String?>()
exportedNodeArray.forEach {
idMap[it["key"].asText()] = localMap[it["value"].asText()]
}
return idMap
}
I am new to Kotlin, and would like to understand a more functional approach. Especially if there is a way to access elements of a JsonNode by attribute value, and accomplish this in a single loop or map call.
If you define one more function, it could look like this:
fun mapIds(exportedNodeArray: JsonNode, localNodeArray: JsonNode) : Map<String, String> {
val localConverted = convert(localNodeArray)
return convert(exportedNodeArray)
.filterKeys(localConverted::containsKey)
.map { it.value to localConverted.getValue(it.key) }
.toMap()
}
fun convert(node: JsonNode): Map<String, String> = node.associate {
it["value"].asText() to it["key"].asText()
}

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
}

Collect iterables into one variable (list)

Consider this:
fun readFiles(directory: String): List<File> {
val result = ArrayList<File>()
File(directory).walkTopDown().forEach {
result.addAll(getFiles(it))
}
return result
}
fun getFiles(file: File): List<File> { ... }
How can rewrite this so I don't need to initialize the result ArrayList but can directly return File(directory).walkTopDown().????
The question is not about what's the best way to read files or anything, just how I can write the above code more concise while doing the same.
You can use flatMap for this purpose. It first maps each element to a Sequence using your mapping function (so you kinda get a Sequence<Sequence<File>>), then it flattens every result back to a Sequence<File>.
Since walkTopDown returns a FileTreeWalk (which is a subclass of Sequence<File>), and you return a List<File>, you have to do some conversions as well. You can remove these conversions if you make getFiles and readFiles return a Sequence<File> instead.
fun readFiles(directory: String): List<File> {
return File(directory)
.walkTopDown()
.flatMap { getFiles(it).asSequence() }
.toList()
}

Is there a way to construct a HashSet with initializator function in Kotlin?

To read Stars from a file in the Facebook Hacker Cup's 2016 Boomerang Constelations problem, following extension function can be defined:
fun BufferedReader.readStars(n: Int): Set<Star> {
return Array(n) {
val (l1, l2) = readLine().split(" ").map { it.toInt() }
Star(l1, l2)
}.toHashSet()
}
Code is compact but the values are first read into an array and then converted to a HashSet. Is there a way to directly initialize a HashSet with the size of n and initializator function in Kotlin?
UPDATE: Is there an existing way in standard Kotlin libs?
You can always use apply to initialize objects in-place:
HashSet<Star>(n).apply {
repeat(n) {
val (l1, l2) = readLine()!!.split(' ').map { it.toInt() }
put(Star(l1, l2))
}
}
If that's too inconvenient too type every time, write an extension function:
inline fun <T> createHashSet(n : Int, crossinline fn: (Int) -> T) = HashSet<T>(n).apply {
repeat(n) { add(fn(it)) }
}
Usage:
createHashSet<Star>(n) {
val (l1, l2) = readLine()!!.split(' ').map { it.toInt() }
Star(l1, l2)
}
Since HashSet is a java class so you can only initialize it in a way provided by JDK.
While there's no helper method in Kotlin runtime it's easy to write it yourself like so:
public fun <T> hashSetOf(size: Int, initializer: (Int) -> T): HashSet<T> {
val result = HashSet<T>(size)
0.rangeTo(size - 1).forEach {
result.add(initializer(it))
}
return result
}
As #miensol has pointed out HashSet initialization is limited to the constructors made available by the JDK. Kotlin has added a hashSetOf function which initializes an empty HashSet and then adds the specified elements to it.
To avoid first reading the values into an array you can use a kotlin.Sequence who's "values are evaluated lazily":
fun BufferedReader.readStars(n: Int): Set<Star> {
return lineSequence().take(n).map {
val (l1, l2) = it.split(" ").map { it.toInt() }
Star(l1, l2)
}.toHashSet()
}
It seems like you are asking an XY question (http://xyproblem.info/). You really want to know how to write readStars in the most efficient way, but instead you ask about HashSet. I think #mfulton26 answers your question as well depending on what is being asked.
Here is the answer for "how do I write this in the most efficient way:"
You have two options. First, a version that auto-closes the stream at the end:
fun BufferedReader.readStars(n: Int): Set<Star> {
return use {
lineSequence().map { line ->
val idx = line.indexOf(' ')
Star(line.substring(0, idx).toInt(), line.substring(idx + 1).toInt())
}.toSet()
}
}
And second, a version that does not:
fun BufferedReader.readStars(n: Int): Set<Star> {
return lineSequence().map { line ->
val idx = line.indexOf(' ')
Star(line.substring(0, idx).toInt(), line.substring(idx+1).toInt())
}.toSet()
}
Neither version creates an array, neither do they make copies of data. They stream the data through a sequence which creates the Set and fills it directly.
Other notes
No need to use split if you are really concerned about allocations and performance. Just use indexOf(char) and split the string yourself using substring.
If you do a split, then please use split(char) not split(String) when you are looking to split on a char