So the idea is you can check your favorite song for a certain band, by choosing the band name but the problem is I can't get it to take user input and return the info when it loops back again so the user can check his new added songs later.
import java.util.*
fun main(args: Array<String>) {
fun again() {
val favSongs = hashMapOf(
"metallica" to "The unforgiven III",
"disturbed" to "The vengeful one",
"godsmack" to "Running blind",
"lambofgod" to "The duke",
"21savage" to "A lot"
)
println(
"Choose a band to display your favorite song of it:\n(please enter the name of the band)\n"
"1.Metallica\n2.Disturbed\n3.Godsmack\n4.Lambofgod\n5.21savage\nEnter Here Please:"
)
val bandName = readln()
when (val bandNameLower: String = bandName.lowercase(Locale.getDefault())) {
"metallica" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
"disturbed" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
"godsmack" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
"lambofgod" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
"21savage" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
else -> println("Enter a valid band name!")
}
println("Do you want to check another favorite song? (1 for Yes\n0 for No\n to add new favorite band & song)")
val anotherSong = readln()
anotherSong.toString()
when (anotherSong) {
"1" -> again()
"0" -> println("Come back soon!")
}
if (anotherSong == " ") {
println("Enter the name of the band:")
val newBandName: String = readln()
println("What's your favorite song for $newBandName")
val newFavSong: String = readln()
favSongs["$newBandName"] = "$newFavSong"
again()
}
}
again()
}
CodePudding user response:
As @cactustictacs mentioned, you need to have your favSongs
map live outside of again()
so that it's not reset each time you call the function.
Also, in order for the add a song flow to work, you need to tweak a few things. I updated the code here (with some comments that'll hopefully help):
fun main() {
// I moved this out of the again() function, plus changed it to Kotlin's MutableMap class
val favSongs = mutableMapOf(
"Metallica" to "The unforgiven III",
"Disturbed" to "The vengeful one",
"Godsmack" to "Running blind",
"LambofGod" to "The duke",
"21Savage" to "A lot"
)
fun again() {
// This now programmatically displays all your artists and songs rather than having them hard-coded.
println(
"Choose a band to display your favorite song of it:\n(please enter the name of the band)\n"
favSongs.keys
.mapIndexed { index, bandName -> "${index 1}. $bandName" }
.joinToString("\n")
)
// The previous toString() call didn't change the original value so I removed it.
// Plus, bandName is already a string so no need to convert.
val bandName = readln()
// I took this approach since we're always doing the same thing (get song by band name)
// Also, this allows the entry to be upper or lower case, we don't care.
val favoriteSong = favSongs.entries.firstOrNull { (band, _) ->
band.equals(bandName, ignoreCase = true)
}?.value
if (favoriteSong != null) {
println("Your favorite song of $bandName is: $favoriteSong")
} else {
println("Enter a valid band name!")
}
println("Do you want to check another favorite song?\n(1 for Yes, 0 for No, to add new favorite band & song)")
val anotherSong = readln()
// The " " scenario can be included in the when { ... } block without issue.
when (anotherSong) {
"1" -> again()
"0" -> println("Come back soon!")
" " -> {
println("Enter the name of the band:")
val newBandName: String = readln()
println("What's your favorite song for $newBandName")
val newFavSong: String = readln()
favSongs[newBandName] = newFavSong
again()
}
}
}
again()
}
CodePudding user response:
(This is longer than I thought because I started running into the complications of what you're doing - I know you're new and I hope this isn't overwhelming, but there are some tricky things that need to be handled!)
Ok so here's one way you can organise the looping:
import java.util.Locale
val favSongs = mutableMapOf(
"metallica" to "The unforgiven III",
"disturbed" to "The vengeful one",
"godsmack" to "Running blind",
"lambofgod" to "The duke",
"21savage" to "A lot"
)
fun main() {
var option: String
// loop until the user types "0" in the options
do {
displayFav()
// show the options menu, and get the result
option = showOptions()
// if the user typed " " we need to let them add a new fav before we loop
if (option == " ") addNewFav()
} while (option != "0")
// You can print this message when you leave the loop
println("Come back soon!")
}
fun displayFav() {
println(
"Choose a band to display your favorite song of it:\n(please enter the name of the band)\n"
"1.Metallica\n2.Disturbed\n3.Godsmack\n4.Lambofgod\n5.21savage\nEnter Here Please:"
)
val bandName = readln()
when (val bandNameLower: String = bandName.lowercase(Locale.getDefault())) {
"metallica" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
"disturbed" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
"godsmack" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
"lambofgod" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
"21savage" -> println("Your favorite song of $bandName is: " favSongs[bandNameLower])
else -> println("Enter a valid band name!")
}
}
fun showOptions(): String {
println("Do you want to check another favorite song?")
println("(1 for Yes\n0 for No\n to add new favorite band & song)")
return readln()
}
fun addNewFav() {
println("Enter the name of the band: ")
val newBandName: String = readln()
println("What's your favorite song for $newBandName: ")
val newFavSong: String = readln()
favSongs[newBandName] = newFavSong
}
The while
loop in main
allows the program to keep running until you hit some condition, like the user entering a "quit" command in this case. So the loop basically gets their input, and checks what they typed at the end. If it's "0"
it quits - anything else and it loops again (i.e. that's the default, as if they typed "1"
)
I've broken out the logic into a few different functions, so it's easier to look at main
and see the big picture of how it's running. The details for each task are in the different functions, and you can tweak what's happening in each of those without worrying about the others.
And as an example of that - your code doesn't exactly work right now, because you're hardcoding your songs menu, and also the list of names it'll accept! Even though you can add songs to the map, you're not making use of the map when it comes to displaying bands or checking for valid names.
Here's how you can do that:
fun displayFav() {
println("Choose a band to display your favorite song of it:\n(please enter the name of the band)\n")
favSongs.keys
.mapIndexed { index, bandName -> "${index 1}. $bandName\n" }
.forEach { println(it) }
val bandName = readln()
val favSong = favSongs[bandName]
if (favSong != null) println("Your favorite song of $bandName is: $favSong")
else println("Enter a valid band name!")
}
So now, we're taking the keys
of the map (the band names) and using mapIndexed
to transform them (along with their index in the map) into a numbered list of items. And we can take what the user typed in, try to fetch it from the map, and if that band exists we get the song - otherwise we get null
and tell them to enter a valid name.
But here's the problem - you're using lowercase
to create a case-insensitive lookup, right? That's fine, but you can't use the band names as keys in the table, and have them lowercase (for lookups) and correctly capitalised (for display). When the user adds their own band, you can lowercase the name to store it, but then you lose the formatting.
You have two options:
- store the lowercase name as the key, mapped to a data structure (like a basic data class) that contains the normally formatted name (for display), and the fav song
- store the normally formatted name as the key, mapped to the fav song, but look up a band by iterating over each entry in the map, lowercasing its key and comparing that to the user's lowercased input
I think the first option is better, but this is getting long enough and going off into all these other tangent concepts already, so I'll just do the "iterate over entries" thing so there aren't too many changes:
fun displayFav() {
println("Choose a band to display your favorite song of it:\n(please enter the name of the band)\n")
favSongs.keys
.mapIndexed { index, bandName -> "${index 1}. $bandName\n" }
.forEach { println(it) }
val bandName = readln().lowercase(Locale.getDefault())
val favSong = favSongs.entries
.firstOrNull { (k, _) -> k.lowercase(Locale.getDefault()) == bandName}
?.value
if (favSong != null) println("Your favorite song of $bandName is: $favSong")
else println("Enter a valid band name!")
}
Now you can just store the band names correctly, and when it comes to the lookup comparison, that's when everything's lowercased. Not the most efficient way to do things (and using data classes gives you the option to easily add more data if you want, say an album name) but for a small map it doesn't really matter.
I wouldn't handle the user options input exactly like that either, but this is enough as it is!