I have a method which takes String as input and returns an Integer corresponding to it as shown below :
fun getPriority(groupValue: String?): Int {
when (groupValue) {
"one" -> return 10
"Two" -> return 9
"THREE" -> return 8
else -> return 4
}
}
My Question is String comparison in this case takes case of string into consideration or ignores case?
when does a equals-comparison, so it is case-sensitive indeed (see also String.equals).
Ignoring case sensitivity can be accomplished in several ways, one of which is already shown by Willi Mentzel... Some other (depending on what you want to accomplish):
fun getPriority(groupValue : String?) = when {
groupValue == null -> /* handle the null first, so you can concentrate null-safe on the rest later */ 4
groupValue.equals("one", ignoreCase = true) -> 10 /* ignoreCase = false is the default */
/* ... */
else -> 4
}
If it's that simple Willis approach will probably suffice you already.
As other answers has stated, the when-expression uses the equals method for comparison. The equals method on String is case sensitive by default.
If you want to compare objects on something else than its equals method, you could create a small wrapper class with it's own implementation of equals. This might be a little overkill in your particular case, but it might be useful in other cases.
The wrapper class:
// In Kotlin 1.3, this class could probably be inlined.
class CaseInsensitiveString(val s: String) {
fun equals(other: Any) =
(other as? CaseInsensitiveString)?.s?.equals(s, ignoreCase = true) ?: false
// Overriding hashCode() just so it's consistent with the equals method.
override fun hashCode() = s.toLowerCase().hashCode()
}
A convenient extension property to wrap a String:
val String.caseInsensitive
get() = CaseInsensitiveString(this)
Now you can do this:
fun getPriority(groupValue: String?): Int {
when (groupValue?.caseInsensitive) {
"one".caseInsensitive -> return 10
"Two".caseInsensitive -> return 9
"THREE".caseInsensitive -> return 8
else -> return 4
}
}
Yes, it is case sensitive, because String.equals is invoked as Roland already said.
To make it case insensitive:
fun getPriority(groupValue: String?) = when (groupValue?.toLowerCase()) {
"one" -> 10
"two" -> 9
"three" -> 8
else -> 4
}
Tip: since when is an expression, you can use the expression body notation for your function.
Alternative:
Use a Map instead of when. This becomes especially handy if your code should be dynamic.
val priorityMapping = mapOf(
"one" to 10,
"Two" to 9,
"THREE" to 8
)
fun getPriority(groupValue: String?): Int {
groupValue?.let {
priorityMapping.forEach { (key, value) ->
if(key.equals(it, true)) {
return value
}
}
}
return 4 // default value
}
As others already stated, when uses equals to form a boolean expression if a scalar (or a list of scalars) is placed in front of the -> sign. Therefore (snippet from official docs of when - block comments are mine):
when (x) {
1 -> print("x == 1") /* same as x.equals(1) or x == 1 */
2 -> print("x == 2") /* same as x.equals(2) or x == 2 */
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
According to the doc of text.equals:
Parameters
ignoreCase - true to ignore character case when comparing characters. By default
false.
Two characters are considered the same ignoring case if at least one
of the following is true:
The two characters are the same (as compared by the == operator)
Applying the method toUpperCase to each character produces the same result
Applying the method toLowerCase to each character produces the same result
So in you case groupValue.equals("one") is the same as groupValue.equals("one", ignoreCase = false) therefore the answer is yes, when you use when on a String by default the comparison will consider casing.
Related
I have the task to create a password validation that has to consider some things. The only problem I have is that one of the criteria of the password validation is that the password must not contain any sequences, e.g. (12345), (abcdef), (asdfghjk). I have already searched a lot and do not know how to implement this. Can anyone help.
This is how I implemented it.
I also check that there is no sequence in backwards, for example (4321, dcba).
private fun noSequenzes(password: String) : Boolean {
val charRuns = listOf(
'0'..'9',
'a'..'z',
'A'..'Z',
"qwertzuiop".asIterable(),
"asdfghjklöä".asIterable(),
"yxcvbnm".asIterable()
)
var map = emptyMap<Char, MutableSet<Char?>>()
charRuns.forEach { run ->
run.forEach { char ->
val charsToAdd = mutableSetOf(run.elementAtOrNull(run.indexOf(char) + 1))
if (run is CharRange) {
charsToAdd.add(run.elementAtOrNull(run.indexOf(char) - 1))
}
if (map.contains(char)) {
map.get(char)!!.addAll(charsToAdd)
}
else {
map = map.plus(Pair(char, charsToAdd))
}
}
}
var sequenceCounter = 1
var recentChar: Char? = null
password.toCharArray().forEach { c ->
recentChar?.let { rc ->
val isSequence = map.any { me -> me.key == rc && me.value.contains(c) }
if (isSequence) {
sequenceCounter = sequenceCounter + 1
}
else {
sequenceCounter = 1
}
if (sequenceCounter >= 3) {
return false
}
}
recentChar = c
}
return true
}
Since you didn't give much detail into what code you already have and what you're stuck on about the logic, here's a very generalized description of a strategy you could use to do this:
Create a List<Iterable<Char>> that contains all the possible strings of characters that could be considered a range. For example:
val charRuns = listOf(
'0'..'9',
'a'..'z',
'A'..'Z',
"qwertyuiop".asIterable(),
//...
)
Iterate these runs to fill a MutableMap<Char, MutableSet<Char>>, where the keys are any of the characters from the runs, and the values are sets of all the chars that if they appear next in a string should be considered a consecutive sequence.
Iterate the potential password String, using the map to check the subsequent Char of each Char to see if it should be considered part of a sequence. Use a counter variable to count the current size of sequence found so far, and reset it whenever a non-sequence is found. If it ever rises above your threshold for allowable sequence size, reject the password immediately.
How should I adjust this fun? When running the app the toast doesn't display the value, but as is. Hope that makes sense. For ex: "Option: #string/about_us" would be displayed instead of that actual value
override fun onOptionsItemSelected(item: MenuItem): Boolean {
var selectedOption = ""
when (item.itemId) {
R.id.about_us -> selectedOption = "#string/about_us"
R.id.help -> selectedOption = "#string/help"
R.id.item_1 -> selectedOption = "#string/item_1"
R.id.item_2 -> selectedOption = "#string/item_2"
R.id.item_3 -> selectedOption = "#string/item_3"
}
val text = "Option: $selectedOption"
val toastiest = Toast.LENGTH_LONG
Toast.makeText(this, text, toastiest).show()
return super.onContextItemSelected(item)
}
You need to use Context#getString(stringResId) to fetch the appropriate string from the ones you've defined (this also handles fetching the appropriate language version if you're using translations). You can't use the #string/item_1 syntax here, that's an XML thing - you need to use R.string.item_1
You already have a Context (you're using it when you make the toast) so here's what I'd recommend:
val selectedOption = when (item.itemId) {
R.id.about_us -> R.string.about_us
R.id.help -> R.string.help
R.id.item_1 -> R.string.item_1
R.id.item_2 -> R.string.item_2
R.id.item_3 -> R.string.item_3
else -> null
}?.let { getString(it) } ?: "fallback message goes here"
So you map various IDs to their string resource ID, and you run getString() with the result, so you only need to write that once instead of repeating it for every line.
By passing null when nothing matches, and null-checking before the let, you can set a fallback string - either an ID matches and gets turned into a string, or you get that string after the ?: elvis operator. Either way, selectedOption gets set to something, so you can make it a val because it's being defined right then and there
I'm studying kotlin, but I'm very disappointed, I can not compare two Strings.
What is the right way to compare.
btn_login.setOnClickListener {
val login = input_email.text.trim()
val pass = input_password.text.trim()
if( login.equals( pass ) ){
startActivity<MainActivity>()
}
if (login?.equals(other = pass)){
startActivity<MainActivity>()
}
if (login == pass){
startActivity<MainActivity>()
}
}
According to documentation for structual equality use ==. It is translated to a?.equals(b) ?: (b === null).
In you case convert login and pass from SpannableStringBuilder to String.
val login = input_email.text.trim().toString()
Here is the example for matching the two strings using kotlin.
If you are using == (double equals) for matching the string then it's compare the address & return maximum time wrong result as per java documentation so use equals for the same
If you want to use equal ignore case then pass the true in the equals method of String
if (s1.equals(s2,true))
other wise you can just use this without boolean like
if (s1.equals(s2,false)) or if (s1.equals(s2))
compleate code is below
fun main(args: Array<String>) {
val s1 = "abc"
val s2 = "Abc"
if (s1.equals(s2,true))
{
println("Equal")
}
else
{
println("Not Equal")
}
}
Covert both the SpannableStringBuilder to string with toString, this should work.
val login = input_email.text.trim().toString()
val pass = input_password.text.trim().toString()
if (login == pass){
startActivity<MainActivity>()
}
1. == :
if ( string1 == string2 ){...}
2. equals :
Indicates whether some other object is "equal to" this one.
Implementations must fulfil the following requirements:
Reflexive: for any non-null reference value x, x.equals(x) should
return true.
Symmetric: for any non-null reference values x and y, x.equals(y)
should return true if and only if y.equals(x) returns true.
Transitive: for any non-null reference values x, y, and z, if
x.equals(y) returns true and y.equals(z) returns true, then
x.equals(z) should return true
Consistent: for any non-null reference values x and y, multiple
invocations of x.equals(y) consistently return true or consistently
return false, provided no information used in equals comparisons on
the objects is modified.
/** * Returns `true` if this string is equal to [other], optionally ignoring character case. * * #param ignoreCase `true` to ignore character case when comparing strings. By default `false`. */
public fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean
3. compareTo :
public override fun compareTo(other: String): Int
Compares this object with the specified object for order. Returns zero
if this object is equal to the specified other object, a negative
number if it's less than other, or a positive number if it's greater
than other.
public fun String.compareTo(other: String, ignoreCase: Boolean = false): Int
Compares two strings lexicographically, optionally ignoring case
differences
i know this is way too late, but as a newbie learning Kotlin, i had the same doubts.
then i came across this wonderful article that articulates the various string comparison types in Kotlin and the differences between them all.
in short both == and .equals() can be used to compare the value of 2 strings in kotlin.
hopefully that helps
With case checking
String a=.....
String b=.....
if(a==b){
}
IgnoreCase
if(a.equals(b,false))
KOTLIN:
if (editText1.text.toString() == editText2.text.toString() ) {
println("Should work now! The same value")
}
Try the following solution, see if it helps:
val passStr: String = textView.text.toString()
if( loginStr.compareTo(passStr, false) ){
startActivity<MainActivity>()
}
Try this surely will work.
val style = buildString { karthik}
val style2 = buildString { karthik }
var result = style.equals(style2)
if(result){//Do something}
This question already has answers here:
How to write ternary conditional operator?
(33 answers)
Closed 4 years ago.
I can write in java
int i = 10;
String s = i==10 ? "Ten" : "Empty";
Even I can pass it in method parameter.
callSomeMethod(i==10 ? "Ten" : "Empty");
How do I convert it to kotlin? Lint shows error when writing same thing in kotlin.
callSomeMethod( if (i==10) "Ten" else "Empty")
Discussion about ternary operator:
https://discuss.kotlinlang.org/t/ternary-operator/2116/3
Instead of
String s = i==10 ? "Ten" : "Empty";
Technically you can do
val s = if(i == 10) "Ten" else "Empty"
val s = when {
i == 10 -> "Ten"
else -> "Empty"
}
val s = i.takeIf { it == 10 }?.let { "Ten" } ?: "Empty"
// not really recommended, just writing code at this point
val s = choose("Ten", "Empty") { i == 10 }
inline fun <T> choose(valueIfTrue: T, valueIfFalse: T, predicate: () -> Boolean) =
if(predicate()) valueIfTrue else valueIfFalse
You can create an extension function with generic for boolean value
fun <T> Boolean.elvis( a :T, b :T ): T{
if(this) // this here refers to the boolean result
return a
else
return b
}
and now you can use it for any boolean value (cool Kotlin)
// output
System.out.print((9>6).elvis("foo","bar")) foo
System.out.print((5>6).elvis("foo","bar")) bar
Extensions in kotlin
As the original question was using the term 'Elvis operator' it may be a good idea to provide a short comparison with the ternary operator.
The main difference between ternary operator and Elvis operator is that ternary is used for a short if/else replacement while the Elvis operator is used for null safety, e.g.:
port = (config.port != null) ? config.port : 80; - a shortcut for an if/then/else statement
port = config.port ?: 80 - provides a default value to be used in case the original one is null
The examples look very similar but the Ternary operator can be used with any type of boolean check while the Elvis operator is a shorthand for use config.port if it's not null else use 80 so it only checks for null.
Now, I think Kotlin doesn't have a ternary operator and you should use an if statement like so - s = if (i==10) "Ten" else "Empty"
There are 4 parameters with default value in function joinToString, in my mind, I should pass parameter value by order when I omit parameter name.
So I think the Code println(letters.joinToString( transform={ it.toLowerCase() } ) ) is right.
But in fact the Code println(letters.joinToString { it.toLowerCase() } ) is right too, why?
fun <T> Collection<T>.joinToString(
separator: String = ", ",
prefix: String = "",
postfix: String = "",
transform: (T) -> String = { it.toString() }
): String {
val result = StringBuilder(prefix)
for ((index, element) in this.withIndex()) {
if (index > 0) result.append(separator)
result.append(transform(element))
}
result.append(postfix)
return result.toString()
}
fun main(args: Array<String>) {
val letters = listOf("Alpha", "Beta")
println(letters.joinToString { it.toLowerCase() } ) //It's Ok
println(letters.joinToString( transform={ it.toLowerCase() } ) ) //It's OK
}
Because you're using a different syntax.
If the last param of a method is a method reference then you can omit the parenthesis and just pass in the function with the { brackets.
it in this case becomes T that you were passing into the function
println(letters.joinToString { it.toLowerCase() } )
Below is what you thought you were entering. This wouldn't compile and would require the named argument or for the params to be in the right order. You would also have to change the syntax from using it to using the regular functional syntax
println(letters.joinToString(it.toLowerCase()))
In addition to #Dan's answer, you don't need to provide a named argument, but if you do so then you're forced to use the named argument for all the following arguments (from the documentation: "all the positional arguments should be placed before the first named one"). In your case the only named argument you're providing is the last one, and all other arguments have default values so you're not forced to provide them, as long as the default value is fine for you.