Hi guys! Quick question. Let us say I have a strin...
# getting-started
c
Hi guys! Quick question. Let us say I have a string array and I have another variable and I am just checking if that variable is in the array. I can directly use the
in
check for that. Is there any way to do the same check while ignoring case check? I know I can do the
any
check, anything besides that?
Copy code
val abc = "abc"
val random = arrayOf("ABC", "egf")

val isThere = abc in random // Prints false
e
Copy code
random.any { abc.equals(it, ignoreCase = true) }
c
Yes, I was just updating the question with this. Is there a more elegant way than this?
e
doesn't seem like Kotlin (or Java for that matter) has any built-in case-folding operations, so in the general case that's probably as good as you can get without additional libraries
c
I see. Maybe I can create an extension function around it to clean up the code around it @ephemient Something like this maybe. I can't come up with a good name though lol
Copy code
fun String.isIgnoreCasePresentIn(input: Array<String>): Boolean {
    return input.any { inp -> inp.equals(this, ignoreCase = true) }
}
e
abc.lowercase() in random.map { it.lowercase() }
,
abc.uppercase() in random.map { it.uppercase() }
, and even
abc.uppercase().lowercase() in random.map { it.uppercase().lowercase() }
will not be quite right for some inputs in some locales (this is where a proper Unicode casefold would be useful), but for most inputs something like that will likely work. if you're doing many membership tests with the same collection, you will probably be better off doing that with a
Set
i
isThere = random.find{it.equals(“abc”, true)}!=null
a
I would just create a wrapper class that only has a contains method that ignores case and you could just do something like this:
isThere = abc in random.ignoringCase()
. Would still require to use e.g. the
any
check, but would look nicer on the use side
m
I’m not seeing the isue with Ephemient’s first answer? It reads like English “are there any where abc equals an element (ignoring the case)” I wouldn’t overcomplicate it
c
Yeah. I followed that and just created an extension function around it, to hide the unnecessary details. This was just an example. In the actual code there is some boilerplate for fetching the particular array to so overall it was a bit tough to read. Hid that under extension function. I guess it will do for now
👍 1
e
as I said earlier, if you're doing many checks with the same check, it would be better to case fold once - then you can use normal string comparison on the results. using ICU4J,
Copy code
val caseFold = CaseMap.fold()
val randomCaseFolded = buildSet { random.mapTo(this) { caseFold.apply(it) } }
caseFold.apply(abc) in randomCaseFolded
but
.lowercase()
or
.uppercase()
works if only have ASCII inputs and
.uppercase().lowercase()
should work for most other than Turkic languages
s
I think if this is a sort of comparison you’re doing often, you might want to optimize on the data structure side of things rather than the input side:
Copy code
val caseInsensitiveSet = TreeSet(String.CASE_INSENSITIVE_ORDER)
    .apply { addAll(setOf("ABC", "egf")) }

"abc" in caseInsensitiveSet  // true
The caveats relating to locale-specific rules on case still apply, though
java.text.Collator
has provisions for locale-aware logic. Also, yes, this particular solution is Java-specific, but I think the approach itself holds across platforms.
🆒 1
1
e
TreeSet is O(log n) lookup vs O(1) for most other sets (including
buildSet()
), but it's probably the best option if you need other collations
a
just implement the operator function like that:
Copy code
fun main() {
  val abc = "abc"
  val random = arrayOf("ABC", "egf")

  println(abc in random) // return true
}

operator fun Array<String>.contains(element: String): Boolean = any {
  it.equals(element, true)
}
c
@Arxing Lin That is a good suggestion. Although it can cause quite some confusion. Since it'd make every check case insensitive by default. I hope I am making sense.
a
you can try
infix
functions like that:
Copy code
fun main() {
  val abc = "abc"
  val random = arrayOf("ABC", "egf")

  println(abc ignoreCaseIn random) // return true
}

infix fun String.ignoreCaseIn(array: Array<String>): Boolean = array.any {
  it.equals(this, true)
}
👌 1
c
Awesome. Thank you!