Read one or two variables alternately in one line - kotlin

I have declared 2 variables to read from console but on other case i want to read just one of them but i can't.
My code:
print("Enter two numbers in format: {source base} {target base} (To quit type /exit) ")
val (sourceBase, targetBase) = readLine()!!.split(" ")
`I can't type /exit because i've got IndexOutOfBoundsException.
Any tips?
Edit: Thank you all for respond, especially lukas.j, it's working now.

Add a second element, an empty string, if the splitted readLine() contains less than 2 elements:
val (sourceBase, targetBase) = readLine()!!.split(" ").let { if (it.size < 2) it + "" else it }

Related

Convert String into list of Pairs: Kotlin

Is there an easier approach to convert an Intellij IDEA environment variable into a list of Tuples?
My environment variable for Intellij is
GROCERY_LIST=[("egg", "dairy"),("chicken", "meat"),("apple", "fruit")]
The environment variable gets accessed into Kotlin file as String.
val g_list = System.getenv("GROCERY_LIST")
Ideally I'd like to iterate over g_list, first element being ("egg", "dairy") and so on.
And then ("egg", "dairy") is a tuple/pair
I have tried to split g_list by comma that's NOT inside quotes i.e
val splitted_list = g_list.split(",(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*\$)".toRegex()).toTypedArray()
this gives me first element as [("egg", second element as "dairy")] and so on.
Also created a data class and tried to map the string into data class using jacksonObjectMapper following this link:
val mapper = jacksonObjectMapper()
val g_list = System.getenv("GROCERY_LIST")
val myList: List<Shopping> = mapper.readValue(g_list)
data class Shopping(val a: String, val b: String)
You can create a regular expression to match all strings in your environmental variable.
Regex::findAll()
Then loop through the strings while creating a list of Shopping objects.
// Raw data set.
val groceryList: String = "[(\"egg\", \"dairy\"),(\"chicken\", \"meat\"),(\"apple\", \"fruit\")]"
// Build regular expression.
val regex = Regex("\"([\\s\\S]+?)\"")
val matchResult = regex.findAll(groceryList)
val iterator = matchResult.iterator()
// Create a List of `Shopping` objects.
var first: String = "";
var second: String = "";
val shoppingList = mutableListOf<Shopping>()
var i = 0;
while (iterator.hasNext()) {
val value = iterator.next().value;
if (i % 2 == 0) {
first = value;
} else {
second = value;
shoppingList.add(Shopping(first, second))
first = ""
second = ""
}
i++
}
// Print Shopping List.
for (s in shoppingList) {
println(s)
}
// Output.
/*
Shopping(a="egg", b="dairy")
Shopping(a="chicken", b="meat")
Shopping(a="apple", b="fruit")
*/
data class Shopping(val a: String, val b: String)
Never a good idea to use regex to match parenthesis.
I would suggest a step-by-step approach:
You could first match the name and the value by
(\w+)=(.*)
There you get the name in group 1 and the value in group 2 without caring about any subsequent = characters that might appear in the value.
If you then want to split the value, I would get rid of start and end parenthesis first by matching by
(?<=\[\().*(?=\)\])
(or simply cut off the first and last two characters of the string, if it is always given it starts with [( and ends in )])
Then get the single list entries from splitting by
\),\(
(take care that the split operation also takes a regex, so you have to escape it)
And for each list entry you could split that simply by
,\s*
or, if you want the quote character to be removed, use a match with
\"(.*)\",\s*\"(.*)\"
where group 1 contains the key (left of equals sign) and group 2 the value (right of equals sign)

how to read mutliple lines of string into one variable using readln() in kotlin?

example:
a variable
val str = readln().replace("[^A-Za-z0-9 ] \\s+".toRegex(),"").trim()
should read multiple lines of input value, input value will be like this
heading
----------
topic1
topic2
or like this
heading
-------
a) topic1
b) topic2
input may contain special characters or tabs or spaces we need to remove them also
I don't know what your Regex is trying to do, but that's not really your question.
How do you know when the user has finished their input - a special word or an empty line?
Assuming an empty line, here's how you can get all the content
println("Enter something:")
var lines = ""
do {
val line = readLine()
lines += "${clean(line)}\n"
} while (!line.isNullOrBlank())
println("User input:\n$lines")
private fun clean(line: String?): String? {
return line?.replace("[^A-Za-z0-9 ] \\s+".toRegex(),"")?.trim()
}

Split character from string in Idiomatic way Kotlin

Hey I am working in kotlin. I have one string in which I want to split into list from there where I should provide character. I'll explain in details
For example 1
val string = "Birth Control"
val searchText = "n"
Output
["Birth Co", "trol"]
For example 2
val string = "Bladder Infection"
val searchText = "i"
Actual Output
["Bladder ", "nfect", "on"]
Expect Output
["Bladder ", "nfection"]
I tried some code but example 1 is working fine but example 2 is not because I only want to split first occurrence.
val splitList = title?.split(searchText, ignoreCase = true)?.toMutableList()
splitList?.remove(searchText)
Can someone guide me how to solve this idiomatic way. Thanks
You miss the limit option of the split function. If you give it a value of 2 the result list will have a maximum of 2 entries:
val result = "Bladder Infection".split("i", ignoreCase = true, limit = 2)

Building string from list of list of strings

I rather have this ugly way of building a string from a list as:
val input = listOf("[A,B]", "[C,D]")
val builder = StringBuilder()
builder.append("Serialized('IDs((")
for (pt in input) {
builder.append(pt[0] + " " + pt[1])
builder.append(", ")
}
builder.append("))')")
The problem is that it adds a comma after the last element and if I want to avoid that I need to add another if check in the loop for the last element.
I wonder if there is a more concise way of doing this in kotlin?
EDIT
End result should be something like:
Serialized('IDs((A B,C D))')
In Kotlin you can use joinToString for this kind of use case (it deals with inserting the separator only between elements).
It is very versatile because it allows to specify a transform function for each element (in addition to the more classic separator, prefix, postfix). This makes it equivalent to mapping all elements to strings and then joining them together, but in one single call.
If input really is a List<List<String>> like you mention in the title and you assume in your loop, you can use:
input.joinToString(
prefix = "Serialized('IDs((",
postfix = "))')",
separator = ", ",
) { (x, y) -> "$x $y" }
Note that the syntax with (x, y) is a destructuring syntax that automatically gets the first and second element of the lists inside your list (parentheses are important).
If your input is in fact a List<String> as in listOf("[A,B]", "[C,D]") that you wrote at the top of your code, you can instead use:
input.joinToString(
prefix = "Serialized('IDs((",
postfix = "))')",
separator = ", ",
) { it.removeSurrounding("[", "]").replace(",", " ") }
val input = listOf("[A,B]", "[C,D]")
val result =
"Serialized('IDs((" +
input.joinToString(",") { it.removeSurrounding("[", "]").replace(",", " ") } +
"))')"
println(result) // Output: Serialized('IDs((A B,C D))')
Kotlin provides an extension function [joinToString][1] (in Iterable) for this type of purpose.
input.joinToString(",", "Serialized('IDs((", "))')")
This will correctly add the separator.

Kotlin algorithm to reverse a given string

I'm creating an algorithm in Kotlin that should reverse a given string and output it, so, for example, the string would be "Hello World! and the output would be "olleH !dlroW. I know there's a function that does this already but I'm practising with loops and if statements so I'm doing it myself.
So far, I've got most of a working solution, the only problem with the code I have in that it only works with odd length strings, because of a while loop. I've put what I have in this post.
I'm stuck on what to change to make this program work with strings that are of an even length, currently with those strings, the program falls into an infinite loop because the condition is never met.
The exclusion part of the program will jump over characters that are included in the exclusion string, I've already tested this and it works fine, provided the string minus the skipped character is not of even length.
fun main() {
val userInput = ""
val exclusion = ""
val wordsInString = userInput.split(" ")
var wordsSize = wordsInString.size
var wordPointer = 0
while (wordPointer < wordsSize) {
var currentWord = wordsInString[wordPointer]
var charArray = currentWord.toCharArray()
var charPointerOne = 0
var charPointerTwo = currentWord.length - 1
while (charPointerOne != charPointerTwo) {
if (exclusion.contains(charArray[charPointerOne])) {
charPointerOne++
} else if (exclusion.contains(charArray[charPointerTwo])) {
charPointerTwo--
} else {
var charToSwtichOne = charArray[charPointerOne]
var charToSwitchTwo = charArray[charPointerTwo]
charArray[charPointerOne] = charToSwitchTwo
charArray[charPointerTwo] = charToSwtichOne
charPointerOne++
charPointerTwo--
}
}
wordPointer++
var outputString = String(charArray)
print(outputString + " ")
}
}
You need to change the loop control condition to use < instead of !=. because in case of even length strings they simply never meet and jump over. for example if you had a String of length two, then on first iteration charPointerOne will have value 0 and charPointerTwo will have value 1, and on the next iteration charPointerOne will be incremented to 1 and charPointerTwo will be decremented to 0 and these values still satisfy the loop control hence the loop continues. So to fix this change your code as
while (charPointerOne < charPointerTwo)