Kotlin check List contain ignore case - list

Having the equals ignore case option
if (bookType.equals(Type.BABY.name, true))
Is there an option to do contain similar with ignore case?
val validTypes = listOf("Kids", "Baby")
if (validTypes.contains(bookType)))
I see there is an option of doing :
if (bookType.equals(Type.BABY.name, true) || bookType.equals(Type.KIDS.name, true))
But I want more elegant way

Could use the is operator with a when expression, and directly with book rather than bookType:
val safetyLevel = when (book) {
is Type.BABY, is Type.KIDS -> "babies and kids"
is Type.CHILD -> "okay for children"
else -> "danger!"
}
See Type checks and casts.

Perhaps you could make the valid types list all uppercase then you can do the following:
You could use map to convert the case for all items in the list e.g.
validTypes.contains(bookType.uppercase())
Or you could use any (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/any.html)
validTypes.any { bookType.uppercase() == it }
If you want to keep the casing of your original list you could do:
validTypes.any { bookType.replaceFirstChar { it.uppercaseChar() } == it }

Related

Casting regex match to String

I created a simple code in Scala that checks whether an input is correctly formatted as HH:mm. I expect the code to result in an Array of valid strings. However, what I'm getting as a result is of type Any = Array(), which is problematic as when I try to print that result I get something like that:
[Ljava.lang.Object;#32a59591.
I guess it's a simple problem but being a Scala newbie I didn't manage to solve it even after a good few hours of googling and trial & error.
val scheduleHours = if (inputScheduleHours == "") {
dbutils.notebook.exit(s"ERROR: Missing param value for schedule hours.")
}
else {
val timePattern = """^((?:[0-30]?[0-9]|2[0-3]):[0-5][0-9])$""".r
val inputScheduleHoursParsed = inputScheduleHours.split(";").map(_.trim)
for (e <- inputScheduleHoursParsed) yield e match {
case timePattern(e) => e.toString
case _ => dbutils.notebook.exit(s"ERROR: Wrong param value for schedule hours: '${inputScheduleHours}'")
}
}
The problem is that some branches return the result you want and others return dbutils.notebook.exit which (I think) returns Unit. Scala must pick a type for the result that is compatible with both Unit and Array[String], and Any is the only one that fits.
One solution is to add a compatible value after the calls to dbutils.notebook.exit, e.g.
val scheduleHours = if (inputScheduleHours == "") {
dbutils.notebook.exit(s"ERROR: Missing param value for schedule hours.")
Array.empty[String]
}
Then all the branches return Array[String] so that will be the type of the result.

Split list when predicate is true

Does Kotlin provide a mutation function to split a list when a specific predicate is true?
In the following example the list should be split when the element is a ..
The result should be of the type List<List<String>>.
// input list
val list = listOf(
"This is", "the", "first sentence", ".",
"And", "now there is", "a second", "one", ".",
"Nice", "."
)
// the following should be the result of the transformation
listOf(
listOf("This is", "the", "first sentence"),
listOf("And", "now there is", "a second", "one"),
listOf("Nice")
)
I need something like list.splitWhen { it == "." }
Does Kotlin provide a mutation function to split a list when a
specific predicate is true?
The closest one I have heard of is partition(), however I don't think it will work in your case.
I have made and have briefly tested 3 higher order extension functions, which gives the same expected output.
Solution 1: Straightforward approach
inline fun List<String>.splitWhen(predicate: (String)->Boolean):List<List<String>> {
val list = mutableListOf<MutableList<String>>()
var needNewList = false
forEach {
string->
if(!predicate(string)){
if(needNewList||list.isEmpty()){
list.add(mutableListOf(string))
needNewList= false
}
else {
list.last().add(string)
}
}
else {
/* When a delimiter is found */
needNewList = true
}
}
return list
}
Solution 2: Pair based approach
inline fun List<String>.splitWhen(predicate: (String)->Boolean):List<List<String>> {
val list = mutableListOf<List<String>>()
withIndex()
.filter { indexedValue -> predicate(indexedValue.value) || indexedValue.index==0 || indexedValue.index==size-1} // Just getting the delimiters with their index; Include 0 and last -- so to not ignore it while pairing later on
.zipWithNext() // zip the IndexValue with the adjacent one so to later remove continuous delimiters; Example: Indices : 0,1,2,5,7 -> (0,1),(1,2),(2,5),(5,7)
.filter { pair-> pair.first.index + 1 != pair.second.index } // Getting rid of continuous delimiters; Example: (".",".") will be removed, where "." is the delimiter
.forEach{pair->
val startIndex = if(predicate(pair.first.value)) pair.first.index+1 else pair.first.index // Trying to not consider delimiters
val endIndex = if(!predicate(pair.second.value) && pair.second.index==size-1) pair.second.index+1 else pair.second.index // subList() endIndex is exclusive
list.add(subList(startIndex,endIndex)) // Adding the relevant sub-list
}
return list
}
Solution 3: Check next value if delimiter found approach
inline fun List<String>.splitWhen(predicate: (String)-> Boolean):List<List<String>> =
foldIndexed(mutableListOf<MutableList<String>>(),{index, list, string->
when {
predicate(string) -> if(index<size-1 && !predicate(get(index+1))) list.add(mutableListOf()) // Adds a new List within the output List; To prevent continuous delimiters -- !predicate(get(index+1))
list.isNotEmpty() -> list.last().add(string) // Just adding it to lastly added sub-list, as the string is not a delimiter
else -> list.add(mutableListOf(string)) // Happens for the first String
}
list})
Simply call list.splitWhen{it=="delimiter"}. Solution 3 looks more syntactic sugar. Apart from it, you can do some performance test to check which one performs well.
Note: I have done some brief tests which you can have a look via Kotlin Playground or via Github gist.

Comparing four UITextFields' text property

I would like to compare four UITextFields' text property to make sure they are not the same. Ie:
tbx1.text = "hello"
tbx2.text = "goodbye"
tbx3.text = "goodnight"
tbx4.text = "hello"
should return a false but
tbx1.text = "hello"
tbx2.text = "goodbye"
tbx3.text = "goodnight"
tbx4.text = "good morning"
should return a true.
I know I could use a long and complicated if statement but I'm hoping there is a better way
One possible solution is to add all Strings to a Set and check the count of the set. If it is 4, all textfields had a different value, if it is less than 4, you had duplicates.
You can even generalise it to work with a different number of text fields as well. You just add all textfields' text property to an array, create a Set from the array then compare the number of elements in the two collections.
let textFieldValues = [tbx1.text!, tbx2.text!, tbx3.text!, tbx4.text!]
let textFieldSet = Set(textFieldValues)
if textFieldSet.count == textFieldValues.count {
print("No duplicates")
} else {
print("Some duplicates")
}

filter/map structure to map/guard structure in Scala

I have a summaryPool mutable map that maps a String to a Summary object.
The function namesToSummary has two parameters, the first one is a series of names (in Iterable[String]), and the second one is the summaryPool. What it does is that it returns a series of Summary that corresponds the names.
It's a little bit more complicated, as the name should be checked using regular expression to extract the information that used to be the key to the summaryPool.
For example, "summary1b" should be checked to get "summary1" and "b"; the "summary1" is the key to the pool. In some cases, there may not be the "b" appended.
My implementation uses isSummaryPool function to filter out wrongly formatted name, or the name that is not in the pool. Then, I use map to get the copy of Summary object in the pool.
import scala.collection.mutable.{Map => mm}
def namesToSummaries(names: Iterable[String], summaryPool: mm[String, Summary]) = {
val namePattern = """([a-zA-Z]+\d+)([a-z])?""".r
def inSummaryPool(name: String) = {
name match {
case namePattern(summaryName, summaryType) => {
if (summaryPool.contains(summaryName)) true
else false
}
case _ => false
}
}
names filter inSummaryPool map { name =>
name match {
case namePattern(summaryName, summaryType) => {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
summaryPool.get(summaryName).get.copy(sType)
}
}
}
}
It works fine, but I don't like the implementation as it checks regular expression matching twice.
I think I can integrate the filter/map into map with guard. In order to do so, I thinK I may need to implement similar to this:
import scala.collection.mutable.{Map => mm}
def namesToSummaries(names: Iterable[String], summaryPool: mm[String, Summary]) = {
val namePattern = """([a-zA-Z]+\d+)([a-z])?""".r
names map { name =>
name match {
case namePattern(summaryName, summaryType) => {
if (summaryPool.contains(summaryName)) {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
summaryPool.get(summaryName).get.copy(sType)
}
else
???
}
case _ => ???
}
}
}
I'm not sure what expression should be given in ??? to teach Scala to ignore these cases.
What might be the solution?
EDIT1
I can think about making a ListBuffer object to add Summary object when necessary.
But, I'm not sure about the case when the pattern does not match.
val list: ListBuffer
names foreach { name =>
name match {
case namePattern(summaryName, summaryType) => {
if (summaryPool.contains(summaryName)) {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
list += summaryPool.get(summaryName).get.copy(sType)
}
}
case _ => ???
}
}
}
EDIT2
From Shadowlands' answer, flatMap with None return works fine.
def namesToSummaries(names: Iterable[String], summaryPool: mm[String, Summary]) = {
val namePattern = """([a-zA-Z]+\d+)([a-z])?""".r
names flatMap { name =>
name match {
case namePattern(summaryName, summaryType) => {
if (summaryPool.contains(summaryName)) {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
Some(summaryPool.get(summaryName).get.copy())
}
else None
}
case _ => None
}
}
}
EDIT3
From Jilen's hint, collect seems to be the good answer to reduce more lines of code.
def namesToSummaries(names: Iterable[String], summaryPool: mm[String, Summary]) = {
val namePattern = """([a-zA-Z]+\d+)([a-z])?""".r
names collect { name =>
name match {
case namePattern(summaryName, summaryType) if (summaryPool.contains(summaryName)) => {
var sType = summaryType
if (sType == null || !(sType == "b" || sType == "l")) sType = "b"
summaryPool.get(summaryName).get.copy()
}
}
}
}
However, this code in IntelliJ 14 shows false positive error: this is a bug report (https://youtrack.jetbrains.com/issue/SCL-9094#).
Instead of calling map on the names, try using flatMap. Wrap your successful cases in Some(...), and the ??? becomes None. The 'flattening' part of the flatMap will reduce the 'mapped' Iterable[Option[String]] back to an Iterable[String], ditching all the None cases.
Edit: I didn't drill into your code quite carefully enough - in the 'successful' case you appear to be doing pure side-effecting stuff (ie. updating the mutable map), not returning a result of any kind.
You could instead return a (summaryName, summaryType) tuple at this point (wrapped in Some) and apply the side-effecting code to the contents of the resulting flatMap (probably my preference as being a slightly more functional style), or simply go back to using map and just write _ (meaning here: 'do nothing - ignore any result') instead of ???.

Is there a better way to pick the first element of a List in Scala

Currently I use this to pick the first element of a list:
def Get_Read_Key =
{
logger.entering (TAG, "Get_Read_Key")
val Retval = if (Read_Key_Available)
{
val Retval = Keystrokes.head
Keystrokes = Keystrokes.tail
Retval
}
else
{
calculator.ui.IKey.No_Key
} // if
logger.exiting (TAG, "Get_Read_Key", Retval)
Retval
} // Get_Read_Key
def Read_Key_Available = Keystrokes.size > 0
But it looks all kind of clumsy — especially the double ´Retval´. Is there a better way of doing this? Or is it just the price to pay for using an immutable list?
Background: The routine is used on a Unit Test Mock class – return types are set.
The following code will get you the first element of Keystrokes list if it's not empty and calculator.ui.IKey.No_Key otherwise:
Keystrokes.headOption.getOrElse( calculator.ui.IKey.No_Key )
P.S. Reassigning Keystrokes to a tail is a definite sign of a bad design. Instead you should use the mentioned by themel already existing iteration capabilities of a list in your algorithm. Most probably using methods such as map or foreach will solve your problem.
P.P.S. You've violated several Scala naming conventions:
variable, value, method and function names begin with lowercase
camelCase is used to delimit words instead of underscore. In fact using underscore for these purposes is greatly discouraged due to Scala having a special treating of that specific character
You're implementing an Iterator on a List, that's already in the standard library.
val it = Keystrokes.iterator
def Read_Key_Available = it.hasNext
def Get_Read_Key = if(it.hasNext) it.next() else calculator.ui.IKey.No_Key
You can use pattern matching:
Keystrokes match {
case h::t =>
KeyStrokes = t
h
case _ =>
calculator.ui.IKey.No_key
}