Seems a pretty simple program, but I can't figure out what I'm doing wrong. Any help is greatly appreciated!
TASK Write a program that reads the first name and the last name of a person, each on a separate line. Then, print the first letter of the first name with a dot and then the last name (with a single space in between): for example, Arthur Dent would be A. Dent
I've tried the following code:
fun main() {
val s1 = readLine()
val s2 = readLine()
println(s1.first() +"." + " " + s2)
}
Which returns an error: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
This is pretty straightforward in Python, but been struggling to solve it in Kotlin.
Thanks in advance!
Looking at the readLine docs it states that:
Return the line read or null if the input stream is redirected to a file and the end of file has been reached. (also if you fake an end file indication)
This means that both s1 and s2 are nullable. You would have seen this if you would have explicitly declared the type of the vals (something that beginners in kotlin should probably always do), as they would have been String?.
So, if we want to get the first letter (or do more or less anything), we need to use the ? safe call to avoid a NPE. The reason you don't get any error with s2 is because the default invocation of .toString will just return a String with the value of null.
Now even if you do add the safe call you will still get an error, that being that you can't concatenate null to a String. And you can simply solve it by using template instead of concatenation (which is actually the prefered way in kotlin). So we will have this:
println("${s1?.first()}. $s2")
Some changes due to the single input requirement. If you only have one input of type John Doe then the above code won't work, because you are reading 2 separate lines, one of which does not exist, making the lastName null. Doing is for a single input needs a bit more work:
val line = readLine()?.split(" ")
val firstNameInitial = line?.getOrNull(0)?.first()
val lastName = line?.getOrNull(1)
println("$firstNameInitial. $lastName")
This assumes that there are no people with a space in their names (not sure if true or not),that nobody will enter a middle name, and that the first and last name are separated by a " " space.
As per Joffrey's comment, we can also use the elvis operator to ensure that the readLine is not null, by throwing our own exception stating that the input is not readable.
val line = (readLine() ?: error("Unexpected end of input, expected first and last name in format \"John Doe\"")).split(" ")
val firstNameInitial = line.getOrNull(0)?.first() // in case we receive an empty string
val lastName = line.getOrNull(1) //in case we only receive one name
println("$firstNameInitial. $lastName")
Related
I am trying to obtain the best delimiter for my CSV file, I've seen answers that find the biggest size of the header row. Now instead of doing the standard method that would look something like this:
val supportedDelimiters: Array<Char> = arrayOf(',', ';', '|', '\t')
fun determineDelimiter(headerRow): Char {
var headerLength = 0
var chosenDelimiter =' '
supportedDelimiters.forEach {
if (headerRow.split(it).size > headerLength) {
headerLength = headerRow.split(it).size
chosenDelimiter = it
}
}
return chosenDelimiter
}
I've been trying to do it with some in-built Kotlin collections methods like filter or maxOf, but to no avail (the code below does not work).
fun determineDelimiter(headerRow: String): Char {
return supportedDelimiters.filter({a,b -> headerRow.split(a).size < headerRow.split(b)})
}
Is there any way I could do it without forEach?
Edit: The header row could look something like this:
val headerRow = "I;am;delimited;with;'semi,colon'"
I put the '' over an entry that could contain other potential delimiter
You're mostly there, but this seems simpler than you think!
Here's one answer:
fun determineDelimiter(headerRow: String)
= supportedDelimiters.maxByOrNull{ headerRow.split(it).size } ?: ' '
maxByOrNull() does all the hard work: you just tell it the number of headers that a delimiter would give, and it searches through each delimiter to find which one gives the largest number.
It returns null if the list is empty, so the method above returns a space character, like your standard method. (In this case we know that the list isn't empty, so you could replace the ?: ' ' with !! if you wanted that impossible case to give an error, or you could drop it entirely if you wanted it to give a null which would be handled elsewhere.)
As mentioned in a comment, there's no foolproof way to guess the CSV delimiter in general, and so you should be prepared for it to pick the wrong delimiter occasionally. For example, if the intended delimiter was a semicolon but several headers included commas, it could wrongly pick the comma. Without knowing any more about the data, there's no way around that.
With the code as it stands, there could be multiple delimiters which give the same number of headers; it would simply pick the first. You might want to give an error in that case, and require that there's a unique best delimiter. That would give you a little more confidence that you've picked the right one — though there's still no guarantee. (That's not so easy to code, though…)
Just like gidds said in the comment above, I would advise against choosing the delimiter based on how many times each delimiter appears. You would get the wrong answer for a header row like this:
Type of shoe, regardless of colour, even if black;Size of shoe, regardless of shape
In the above header row, the delimiter is obviously ; but your method would erroneously pick ,.
Another problem is that a header column may itself contain a delimiter, if it is enclosed in quotes. Your method doesn't take any notice of possible quoted columns. For this reason, I would recommend that you give up trying to parse CSV files yourself, and instead use one of the many available Open Source CSV parsers.
Nevertheless, if you still want to know how to pick the delimiter based on its frequency, there are a few optimizations to readability that you can make.
First, note that Kotlin strings are iterable; therefore you don't have to use a List of Char. Use a String instead.
Secondly, all you're doing is counting the number of times a character appears in the string, so there's no need to break the string up into pieces just to do that. Instead, count the number of characters directly.
Third, instead of finding the maximum value by hand, take advantage of what the standard library already offers you.
const val supportedDelimiters = ",;|\t"
fun determineDelimiter(headerRow: String): Char =
supportedDelimiters.maxBy { delimiter -> headerRow.count { it == delimiter } }
fun main() {
val headerRow = "one,two,three;four,five|six|seven"
val chosenDelimiter = determineDelimiter(headerRow)
println(chosenDelimiter) // prints ',' as expected
}
I am new at Kotlin (and my English is terrible :).
I want to define the size of the array and elements in it by inputting it from the keyboard.
fun main() {
val array_size = readLine()!!.toInt()
val arr = IntArray(array_size)
for (i in 0..array_size){
arr[i] = readLine()!!.toInt()
}
for(i in 0..array_size){
println(arr[i])
}
}
[I got this message][1]
[1]: https://i.stack.imgur.com/DRk9F.png
This is my first question in StackOverFlow tho, hope it is understandable for those who want to help.
The NullPointerException is probably because the call to readLine() is returning null and then you're forcing that to non-null using readLine()!! which gives you the NPE.
In recent versions of Kotlin a new method was introduced: readln(). It is recommended to use that instead of the old readLine. If and END OF FILE is found, the new readln method will throw a more descriptive exception, whereas readLine will return null which makes it more difficult to see where you went wrong.
You might get an end-of-file condition if the input is redirected from a file or other source. This often happens if you run your program in an IDE or an online compiler service. If you run your program from the command line, it will work, until you get to enter the last line. This is because for(i in 0..array_size) includes the value array_size which is 1 more than the last index, so you get an out-of-bounds exception.
Instead of using 0..(array_size - 1), it is recommended to use arr.indices which gives you the range of valid indices for the array.
readLine() is returning null, and so when you do readLine!!... you're getting a NullPointerException. Perhaps you want to use readln instead.
I started learning Kotlin a few days ago and I don't really understand why in this case you should use readln()!!.toInt(), from where the number is taken and to which "line" it refers.
fun main(args: Array) {
var number: Int
var sum = 0
for (i in 1..6) {
print("Enter an integer: ")
number = readln()!!.toInt()
It would be extremely helpful if someone could explain that.
Thank you!
readline() (not readln()) returns a line from the standard input stream, or null if the input stream has been redirected to a file and EOF (end of file) is reached. Its return type is String? meaning it could either be a String, or it could be null.
!! is "We are sure this will never be null; force this into a non-nullable type." It means you don't have to deal with the condition where the object you're working with is null.
In this case, the !! is saying "We're sure that system input hasn't been redirected to a file (or if it has, that EOF hasn't been reached); wait for the user to input something."
It looks like someone has mixed up readline() and readln() (which I did when I first answered this question) - #Tenfour04 is right; there's no need to use !! with readln. It does the same thing, except it throws an exception if EOF is encountered.
fun main(args: Array<String>) {
//receive names from user
print("Enter the names: ")
val names = readLine()?.split("/n")
//receive number of groups from the user
print("Enter number of groups: ")
val group = readLine()
print(names.chunked(group))
}
I was trying to print the users into evenly separated groups. I found that I could use chunked to accomplish this. But when I try to input the amount of groups to the chunked function nothing happens. it outputs "kotlin.Unit" I'm guessing this is an error code. But I don't know how I messed up my code.
As mentioned in the comments, this code does not compile. It still has the following problems:
names can be null, so you'll either have to ensure that it isn't, which you could do using the Elvis operator:
val names = readLine()?.split("\n") ?: return
Or you can use a safe call on names wherever you use it:
names?.chunked(group)
If you look at the documentation for chunked, you'll see that it has one parameter, size of type Int. However, you are passing a String to it, which you've read from the user. You'll have to try to convert this string to an int before passing it into chunked:
val group = readLine()?.trim()?.split(" ")?.first()?.toInt() ?: return
Note that I'm trimming and splitting the user input to increase chances of retrieving a valid int.
Fixing these problems makes your code compile. However, there's one more thing to fix. Assuming you meant the newline character with /n, you are now using the newline character as a separator for the names as well as to indicate that the user is done entering names. I suggest you use something different, like a comma. In total, this could give something like the following.
fun main() {
//receive names from user
print("Enter the names (separated by a comma): ")
val names = readLine()?.split(",")?.map { it.trim() }
//receive number of groups from the user
print("Enter number of groups: ")
val group = readLine()?.trim()?.split(" ")?.firstOrNull()?.toInt() ?: return
print(names?.chunked(group))
}
Let's take a string var str = "Hello Kotlin". I want to copy first 5 character of str to another variable strHello. I was wondering is there any function of doing this or I have to apply a loop and copy characters one by one.
As Tim commented, there's a substring() method which does exactly this, so you can simply do:
val strHello = str.substring(0, 5)
(The first parameter is the 0-based index of the first character to take; and the second is the index of the character to stop before.)
There are many, many methods available on most of the common types. If you're using an IDE such as IDEA or Eclipse, you should see a list of them pop up after you type str.. (That's one of many good reasons for using an IDE.) Or check the official documentation.
Please use the string.take(n) utility.
More details at
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/take.html
I was using substring in my project, but it gave an exception when the length of the string was smaller than the second index of substring.
val name1 = "This is a very very long name"
// To copy to another string
val name2 = name1.take(5)
println(name1.substring(0..5))
println(name1.substring(0..50)) // Gives EXCEPTION
println(name1.take(5))
println(name1.take(50)) // No EXCEPTION