kotlin string helpers to find index in a string where any element of another string matches first/last etc - kotlin

C++ has string functions like find_first_of(), find_first_not_of(), find_last_of(),
find_last_not_of(). e.g. if I write
string s {"abcdefghi"};
s.find_last_of("eaio") returns the index of i
s.find_first_of("eaio") returns the index of a
s.find_first_not_of("eaio") returns the index of b
Does Kotlin has any equivalent.

Kotlin doesn't have these exact functions, but they are all special cases of indexOfFirst and indexOfLast:
fun CharSequence.findFirstOf(chars: CharSequence) = indexOfFirst { it in chars }
fun CharSequence.findLastOf(chars: CharSequence) = indexOfLast { it in chars }
fun CharSequence.findFirstNotOf(chars: CharSequence) = indexOfFirst { it !in chars }
fun CharSequence.findLastNotOf(chars: CharSequence) = indexOfLast { it !in chars }
These will return -1 if nothing is found.
Usage:
val s = "abcdefghi"
val chars = "aeiou"
println(s.findFirstOf(chars))
println(s.findFirstNotOf(chars))
println(s.findLastOf(chars))
println(s.findLastNotOf(chars))
Output:
0
1
8
7

Related

Count the number of punctuation symbols in a string Kotlin

I have a question how can I count the number of punctuation symbols in a string.
Here is my code snippet
fun punctuationCount(stringValue: Editable): Int {
val trimmedStr = stringValue.trim()
return if (trimmedStr.isEmpty()) {
0
} else {
return trimmedStr.split("[a-zA-Z&&[ \t]]".toRegex()).size
}
}
You can make use of the built-in count function on CharSequence to count the occurrence of arbitrary characters.
val testString = "Some string containing punctuation, like !, #, #, etc."
val punctuationChars = setOf('!', '#', '#', '$', '%', '.')
val occurrences = testString.count { char -> char in punctuationChars }
println("I've counted $occurrences occurrences!")
If you need this functionality more often, you might want to extract it into an extension function, e.g.
fun CharSequence.countCharacters(searchedCharacters: Set<Char>): Int =
this.count { char -> char in searchedCharacters }
val punctuationChars = setOf('!', '#', '#', '$', '%', '.')
fun CharSequence.countPunctuationCharacters(): Int =
this.countCharacters(punctuationChars)
Then you can do
val occurrences = testString.countPunctuationCharacters()
Thanks to #CryptoFool for the example string.
Here's a version of your function that demonstrates what you're asking for:
fun punctuationCount(stringValue: String): Long {
val trimmedStr = stringValue.trim()
return if (trimmedStr.isEmpty()) {
return 0
} else {
val exp = Pattern.compile("\\p{Punct}")
val matcher = exp.matcher(trimmedStr)
return matcher.results().count()
}
}
If you don't like the Pattern library's definition of what constitutes punctuation, you can replace the line that computes the Pattern object with something like this that lets you define punctuation characters explicitly:
val exp = Pattern.compile("[!##\$%.]")
Another variation - the Unicode standard defines a bunch of categories (see 4.4) that a character can fall into, and those are present in Kotlin, so you could make use of those if you like
import kotlin.text.CharCategory.*
val punctuation = setOf(
DASH_PUNCTUATION, START_PUNCTUATION, END_PUNCTUATION,
CONNECTOR_PUNCTUATION, OTHER_PUNCTUATION
)
val testString = "Some string containing punctuation, like !, #, #, etc."
fun main() {
testString.count { it.category in punctuation }.run(::println)
}
>>> 8
You'd have to decide if those cover your needs, or which ones to include and exclude (and symbols are a different category class). Just throwing it out there as an option / something to know about!

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

Idiomatic way to generate a random alphanumeric string in Kotlin

I can generate a random sequence of numbers in a certain range like the following:
fun ClosedRange<Int>.random() = Random().nextInt(endInclusive - start) + start
fun generateRandomNumberList(len: Int, low: Int = 0, high: Int = 255): List<Int> {
(0..len-1).map {
(low..high).random()
}.toList()
}
Then I'll have to extend List with:
fun List<Char>.random() = this[Random().nextInt(this.size)]
Then I can do:
fun generateRandomString(len: Int = 15): String{
val alphanumerics = CharArray(26) { it -> (it + 97).toChar() }.toSet()
.union(CharArray(9) { it -> (it + 48).toChar() }.toSet())
return (0..len-1).map {
alphanumerics.toList().random()
}.joinToString("")
}
But maybe there's a better way?
Since Kotlin 1.3 you can do this:
fun getRandomString(length: Int) : String {
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
return (1..length)
.map { allowedChars.random() }
.joinToString("")
}
Lazy folks would just do
java.util.UUID.randomUUID().toString()
You can not restrict the character range here, but I guess it's fine in many situations anyway.
Assuming you have a specific set of source characters (source in this snippet), you could do this:
val source = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
java.util.Random().ints(outputStrLength, 0, source.length)
.asSequence()
.map(source::get)
.joinToString("")
Which gives strings like "LYANFGNPNI" for outputStrLength = 10.
The two important bits are
Random().ints(length, minValue, maxValue) which produces a stream of length random numbers each from minValue to maxValue-1, and
asSequence() which converts the not-massively-useful IntStream into a much-more-useful Sequence<Int>.
Using Collection.random() from Kotlin 1.3:
// Descriptive alphabet using three CharRange objects, concatenated
val alphabet: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
// Build list from 20 random samples from the alphabet,
// and convert it to a string using "" as element separator
val randomString: String = List(20) { alphabet.random() }.joinToString("")
Without JDK8:
fun ClosedRange<Char>.randomString(length: Int) =
(1..length)
.map { (Random().nextInt(endInclusive.toInt() - start.toInt()) + start.toInt()).toChar() }
.joinToString("")
usage:
('a'..'z').randomString(6)
To define it for a defined length:
val randomString = UUID.randomUUID().toString().substring(0,15)
where 15 is the number of characters
('A'..'z').map { it }.shuffled().subList(0, 4).joinToString("")
Using Kotlin 1.3:
This method uses an input of your desired string length desiredStrLength as an Integer and returns a random alphanumeric String of your desired string length.
fun randomAlphaNumericString(desiredStrLength: Int): String {
val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
return (1..desiredStrLength)
.map{ kotlin.random.Random.nextInt(0, charPool.size) }
.map(charPool::get)
.joinToString("")
}
If you prefer unknown length of alphanumeric (or at least a decently long string length like 36 in my example below), this method can be used:
fun randomAlphanumericString(): String {
val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
val outputStrLength = (1..36).shuffled().first()
return (1..outputStrLength)
.map{ kotlin.random.Random.nextInt(0, charPool.size) }
.map(charPool::get)
.joinToString("")
}
Or use coroutine API for the true Kotlin spirit:
buildSequence { val r = Random(); while(true) yield(r.nextInt(24)) }
.take(10)
.map{(it+ 65).toChar()}
.joinToString("")
Building off the answer from Paul Hicks, I wanted a custom string as input. In my case, upper and lower-case alphanumeric characters. Random().ints(...) also wasn't working for me, as it required an API level of 24 on Android to use it.
This is how I'm doing it with Kotlin's Random abstract class:
import kotlin.random.Random
object IdHelper {
private val ALPHA_NUMERIC = ('0'..'9') + ('A'..'Z') + ('a'..'z')
private const val LENGTH = 20
fun generateId(): String {
return List(LENGTH) { Random.nextInt(0, ALPHA_NUMERIC.size) }
.map { ALPHA_NUMERIC[it] }
.joinToString(separator = "")
}
}
The process and how this works is similar to a lot of the other answers already posted here:
Generate a list of numbers of length LENGTH that correspond to the index values of the source string, which in this case is ALPHA_NUMERIC
Map those numbers to the source string, converting each numeric index to the character value
Convert the resulting list of characters to a string, joining them with the empty string as the separator character.
Return the resulting string.
Usage is easy, just call it like a static function: IdHelper.generateId()
I use the following code to generate random words and sentences.
val alphabet: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
val randomWord: String = List((1..10).random()) { alphabet.random() }.joinToString("")
val randomSentence: String = (1..(1..10).random()).joinToString(" ") { List((1..10).random()) { alphabet.random() }.joinToString("") }
the question is old already, but I think another great solution (should work since Kotlin 1.3) would be the following:
// Just a simpler way to create a List of characters, as seen in other answers
// You can achieve the same effect by declaring it as a String "ABCDEFG...56789"
val alphanumeric = ('A'..'Z') + ('a'..'z') + ('0'..'9')
fun generateAlphanumericString(length: Int) : String {
// The buildString function will create a StringBuilder
return buildString {
// We will repeat length times and will append a random character each time
// This roughly matches how you would do it in plain Java
repeat(length) { append(alphanumeric.random()) }
}
}
fun randomAlphaNumericString(#IntRange(from = 1, to = 62) lenght: Int): String {
val alphaNumeric = ('a'..'z') + ('A'..'Z') + ('0'..'9')
return alphaNumeric.shuffled().take(lenght).joinToString("")
}
Building on Fabb111's answer:
fun randomString(length: Int): String =
buildString {
repeat(length) {
append((0 until 36).random().toString(36))
}
}
Numbers can be converted to a different base using the Int.toString(Int).
Base 36 is the numeral system that contains all alphanumeric characters
You can use RandomStringUtils.randomAlphanumeric(min: Int, max: Int) -> String from apache-commons-lang3
Here's a cryptographically secure version of it, or so I believe:
fun randomString(len: Int): String {
val random = SecureRandom()
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray()
return (1..len).map { chars[random.nextInt(chars.size)] }.joinToString("")
}
I used the most voted answer, but after using it for a while I discovered a big problem, since the random strings weren't that random.
I had to modify the generation of the random character, since if I only left allowedChars.random() it always generated the same strings, and I couldn't find why. I was reading that because random() was called very quickly, but I don't know how it works when calling it on the list of numbers.
fun getRandomString(): String {
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
var string = ""
for (i in (1..20)) {
string += allowedChars[(Math.random() * (allowedChars.size - 1)).roundToInt()]
}
return string
}
// initialise at top
companion object {
private const val allowedCharacters = "0123456789QWERTYUIOPASDFGHJKLZXCVBNM"
}
//pass size and get Random String Captcha
fun getRandomCaptcha(sizeOfRandomCaptcha: Int): String {
val random = Random()
val sb = StringBuilder(sizeOfRandomCaptcha)
for (i in 0 until sizeOfRandomCaptcha)
sb.append(allowedCharacters[random.nextInt(allowedCharacters.length)])
return sb.toString()
}
Here's a simple function that uses newer Kotlin stdlib functions:
fun randomString(length: Int): String =
CharArray(length) { validChars.random() }.concatToString()
val validChars: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9'),
This uses:
a collection constructor with a initializer function
CharArray(length) { i -> /* provide a value for element i */ }
This is a one-liner for creating a CharArray and initializing each value using a lambda, It accepts a length, so the CharArray will be initialized with the correct size.
The Collection<>.random() extension function, that will pick a random character. This can be seeded if desired.
validChars.random()
The initialized CharArray is converted to a string using concatToString(), which was released in version 1.4.
The best way I think:
fun generateID(size: Int): String {
val source = "A1BCDEF4G0H8IJKLM7NOPQ3RST9UVWX52YZab1cd60ef2ghij3klmn49opq5rst6uvw7xyz8"
return (source).map { it }.shuffled().subList(0, size).joinToString("")
}

How to replace all occurrences of a sub string in kotlin

How to replace a part of a string with something else in Kotlin?
For example, changing "good morning" to "good night" by replacing "morning" with "night"
fun main(args: Array<String>) {
var a = 1
// simple name in template:
val s1 = "a is $a"
a = 2
// arbitrary expression in template:
val s2 = "${s1.replace("is", "was")}, but now is $a"
println(s2)
}
OUPUT: a was 1, but now is 2
"Good Morning".replace("Morning", "Night")
It's always useful to search for functions in the Kotlin standard library API reference. In this case, you find the replace function in Kotlin.text:
/**
* Returns a new string with all occurrences of [oldChar] replaced with [newChar].
*/
public fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String {
if (!ignoreCase)
return (this as java.lang.String).replace(oldChar, newChar)
else
return splitToSequence(oldChar, ignoreCase ignoreCase).joinToString(separator = newChar.toString())
}

Filter a substring in kotlin

In kotlin I'd like to filter a string and return a substring of only valid characters. Say we have valid characters,
valid = listOf('A', 'B', 'C')
How can I define a fcn in kotlin in the most succinct way to filter a string and only retain valid characters? For example,
'ABCDEBCA' --> 'ABCBCA'
'AEDC' --> 'AC'
Having trouble finding a canonical way to do this without resorting to using an array of string.
import kotlin.text.filter
class Test(){
val VALID = listOf("A", "B", "C")
fun filterString(expression: String): String{
expression.filter(x --> !VALID.contains(x)) #Doesn't work
}
}
The filter docs doesn't show any examples specifically for spring manipulation.
val VALID = setOf('A', 'B', 'C') // lookup in a set is O(1), whereas it's O(n) in a list. The set must contain Chars, not Strings
val expression = "ABCDEFEDCBA"
val filtered = expression.filter { VALID.contains(it) }
println(filtered)
// ABCCBA
Or
val VALID = setOf('A', 'B', 'C')
fun filterString(expression: String) = expression.filter { it in VALID }
fun main(args: Array<String>) {
val expression = "ABCDEFEDCBA"
val filtered = filterString(expression)
println(filtered)
// ABCCBA
}
In case you have a long set of chars you could join them in a String and convert it to a Set:
val VALID = "ABC".toSet()
fun filterString(expression: String) = expression.filter { it in VALID }
fun main(args: Array<String>) {
val expression = "ABCDEFEDCBA"
val filtered = filterString(expression)
println(filtered)
// ABCCBA
}