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
}
Related
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.)
Hallo Coding Friends,
i working since a week on a Kotlin Challange.
The Challange is to remove a letter from a Word but it must be with "Operator Overloading" and "for" Loop.
I already done it with filter method and it works but is doesnt finish the Challange.
My Programm - Theoretical i know it can be done but practice is another World
operator fun String.minus(filter1: String): String {
return filter1.minus("l")
}
fun main() {
val wortHW1 = "Hallo"
val wortHW2 = "Hallo World"
for (x in wortHW1) {
x.minus('l')
}
}
I really hope you all can help me, stuck since a weeks.
Thx for all your effort
The buildString function is a useful way to write concise String manipulation code. For example:
operator fun String.minus(removedChar: Char): String = buildString {
for (c in this#minus) {
if (c != removedChar) append(c)
}
}
If I understood correctly and given the requiremets this is what you are looking for:
fun main() {
val wortHW1 = "Hallo"
val wortHW2 = "Hallo World"
println(wortHW1.minus('l'))
println(wortHW2.minus(' '))
}
operator fun String.minus(letterToRemove: Char): String {
val lettersToKeep: MutableList<Char> = mutableListOf()
for (letter in this) {
if (letter != letterToRemove) {
lettersToKeep.add(letter)
}
}
return lettersToKeep.joinToString("")
}
... or the equivalent of java.util.Function.andThen()
In Java
Function<String, String> add1 = string -> string + "1";
Function<String, String> add2 = string -> string + "2";
Function<String, Strint> add12 = add1.andThen(add2);
add12.apply("") returns "12"
How would I write it in Kotlin?
val add1 = { string:String -> string + "1" }
val add2 = { string:String -> string + "2" }
val add12 = ?
The feature you're looking for is called function composition. As far as I can tell, it doesn't come built-in to Kotlin (would love to be corrected on this). But it's very easy to write as an extension function.
infix fun<A, B, C> ((B) -> C).compose(that: (A) -> B): (A) -> C =
{ this(that(it)) }
Now we can write
val add1 = { string:String -> string + "1" }
val add2 = { string:String -> string + "2" }
println((add2 compose add1)("3")) // Prints "312"
I write compose to use right-to-left composition, more in line with the way mathematical functions work.
Granted, this is not exactly what you're looking for, because you can't store a composed function this way in a variable, but you can chain the results of functions using run if the functions themselves don't have the parameter as a receiver:
fun print(string: String) {
println(add1(string).run(add2))
}
// or
fun print(string: String) {
println(string.run(add1).run(add2))
}
Since run is an inline function, it doesn't add a wrapper object around each function.
The let function will have the exact same effect. This is because when you pass something other than a lambda to a higher order function, it doesn't matter if the first parameter is a receiver or not. They are treated as the same signature.
If you are that familiar with Java functions and/or want to use them, you are still open to do that (using java.util.function.Function):
val add1 : Function<String, String> = Function { "${it}1"}
val add2 : Function<String, String> = Function { "${it}2"}
val add12: Function<String, String> = add1.andThen(add2)
If I wanted to have something similar in Kotlin, I would probably just go for what also Tenfour04 showed, i.e. use either let or run:
val add1 : (String) -> String = { "${it}1"}
val add2 : (String) -> String = { "${it}2"}
val add12 : (String) -> String = { it.let(add1).let(add2) } // or: { add1(it).let(add2) }
If you compare the two you only spare something, if you omit the type, but it's still clear enough what gets composed.
Of course you can implement your own compose or andThen-functions. However, if you don't mind using an additional library, you may rather be interested in Arrow where lots of functional use-cases are already supported.
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.
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