How to write a code in Kotlin, that prints three text lines in a column (first second third) - kotlin

I've tried these two options and the IDE returns both options to be correct. However, when I copy/paste the same codes in Hyperskill Studying platform, it says "incorrect".
I can't understand where I am going wrong. Appreciate your help.
The three lines are:
first
second
third
These must return as columns
Solution 1
fun main() {
print("first ")
print("second ")
print("third")
}
Solution_2
fun main() {
val column1 = "first"
val column2 = "second"
val column3 = "third"
val header = column1 + " " + column2 + " " + column3
println(header)
}

You could use String.format here:
values in a row
fun main() {
val column1 = "first"
val column2 = "second"
val column3 = "third"
val rowPattern = "%7s%7s%7s"
println(rowPattern.format(column1, column2, column3))
}
Output:
first second third
values in a column
fun main() {
val column1 = "first"
val column2 = "second"
val column3 = "third"
val colPattern = "%7s\n%7s\n%7s"
println(colPattern.format(column1, column2, column3))
}
Output
first
second
third
Please note: The 7 in the pattern Strings defines the amount of characters used for the argument. The example Strings all have a length < 7, which leads to leading whitespaces to the left of each argument in the pattern. You can reverse this order if you write -7…
e.g. the (row) pattern "%-7s%-7s%-7s" will produce
first second third
You can also just use the length of the arguments by not providing any number, so a pattern of "%s\n%s\n%s" will just put every argument in a new line without leading or trailing whitespaces.

print("first\nsecond\nthird")

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)

Read one or two variables alternately in one line

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 }

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)

In Kotlin, what is the best way to make `String? + String?` return `null` if any of the inputs are `null`?

The problem:
Consider the following code:
val key: String? = "key"
val value: String? = "label"
val row = key + ": " + value
I would like the variable row to be null if any of the supplied inputs in the concatenation is null.
By default, any null String will be converted to "null" and the concatenation will proceed. In example:
val value = null
"Height: " + value + "mm" // Produces: "Height: nullmm"
I can skip the showing "null" in the results by using value ?: "", but this solves only a part of my problem:
val value = null
"Height: " + (value ?: "") + "mm" // Produces: "Height: mm"
My best solution so far:
I understand that writing a simple function like the one below would do the job, but I still expect that something like this already exists in the language:
fun Array<String?>.nullSafeConcat(): String? {
val result = StringBuilder()
this.forEach {
if(it == null) return null
result.append(it)
}
return result.toString()
}
The ask:
Is there a better way to do this?
Post Scriptum:
I cannot understand why would a null string be converted to "null" by default, as I cannot find any use case where this would be actually usable.
I think matt freake's answer is correct for the question, but I would avoid overriding the + operator for Strings - it can cause tons of unexpected issues.
Instead, I suggest you to slightly modify your nullSafeConcat helper function to be a standalone function that takes vararg instead of being an extension function. Something like this:
fun nullSafeConcat(vararg strings: String?): String? {
val result = StringBuilder()
strings.forEach {
if(it == null) return null
result.append(it)
}
return result.toString()
}
Then you can use it like:
val merged = nullSafeConcat("foo", null, "bar", "baz")
Notes:
You might want to handle the empty case (when varargs argument strings is empty) specifically, depending on the outcome you want.
Additionally, if you want this to work for at least 2 strings (so a concatenation is actually meaningful), you can use a signature like nullSafeConcat(first: String?, second: String?, vararg rest: String?) instead.
I'm not sure if this solves the problem, you can override the + operator on a nullable String to get close to what you want. For example:
private operator fun String?.plus(otherString: String?): String? = if (this==null || otherString ==null ) "null" else this + otherString
Then:
fun main() {
val s1: String? = "Hello"
val s2: String? = null
val s3: String? = "Bye"
println(s1 + s2)
println(s2 + s1)
println(s1 + s3)
}
prints:
null
null
HelloBye
The problem is it will only work with variables of String? not String which your ":" value is. So you'd need to do something like:
s1 + colon + s2
where colon was also of type String?
EDIT:
There are two dangers with this approach. Firstly If you don't make it private (or even if you do) there is a risk that existing or new code tries to append two String? values and gets your new behaviour, which they don't expect. Secondly, someone reading the code where you call it may be surprised by the behaviour if they don't spot that you've overridden the + operator.
How about this
listOfNotNull("foo", null, "bar", "baz").joinToString()

Kotlin: split string from end

I want to split I_have_a_string into I_have_a and string. Is there a built in function to split from end in Kotlin? The following is what I am doing now
val words = myString.split("_")
val first = words.dropLast(1).joinToString("_")
val second = words.last()
Look at this:
val myString = "I_have_a_string"
val first = myString.substringBeforeLast("_")
val second = myString.substringAfterLast("_")
I think its self explanatory