Kotlin. Expecting when-condition. How to fix? - kotlin

I am writing a kotlin parser library and faced the following problem.
My code
fun main(args : Array<String>) {
val test = "jap"
val url = when (test) {
"jap" -> "https://staticlib.me/manga-list?types[]=10",
"cor" -> "https://staticlib.me/manga-list?types[]=11",
"chi" -> "https://staticlib.me/manga-list?types[]=12",
"eng" -> "https://staticlib.me/manga-list?types[]=13",
"rus" -> "https://staticlib.me/manga-list?types[]=14",
"fan" -> "https://staticlib.me/manga-list?types[]=15",
"all" -> "https://staticlib.me/manga-list",
else -> "https://staticlib.me/manga-list?types[]=10"
}
println(url)
}
Err message
compiler.kotlin:11:62: error: expecting a when-condition
"jap" -> "https://staticlib.me/manga-list?types[]=10",
^
compiler.kotlin:12:62: error: expecting a when-condition
"cor" -> "https://staticlib.me/manga-list?types[]=11",
^
etc.
how can i solve it?

You have to remove commas inside the when expression:
fun main(args : Array<String>) {
val test = "jap"
val url = when (test) {
"jap" -> "https://staticlib.me/manga-list?types[]=10"
"cor" -> "https://staticlib.me/manga-list?types[]=11"
"chi" -> "https://staticlib.me/manga-list?types[]=12"
"eng" -> "https://staticlib.me/manga-list?types[]=13"
"rus" -> "https://staticlib.me/manga-list?types[]=14"
"fan" -> "https://staticlib.me/manga-list?types[]=15"
"all" -> "https://staticlib.me/manga-list"
else -> "https://staticlib.me/manga-list?types[]=10"
}
println(url)
}
You can also improve your code as follows:
fun main(args : Array<String>) {
val test = "jap"
var url = "https://staticlib.me/manga-list"
if (test != "all") url += "?types[]=" + when (test) {
"jap" -> "10"
"cor" -> "11"
"chi" -> "12"
"eng" -> "13"
"rus" -> "14"
"fan" -> "15"
else -> "10"
}
println(url)
}
EDIT
As suggested by gidds in the comments, here is another way to improve the code:
fun main(args : Array<String>) {
val test = "jap"
val types = when (test) {
"jap" -> "10"
"cor" -> "11"
"chi" -> "12"
"eng" -> "13"
"rus" -> "14"
"fan" -> "15"
"all" -> null
else -> "10"
}
val url = "https://staticlib.me/manga-list" + types?.let{ "?types[]=$it" }
println(url)
}

Related

Kotlin - Readln() gets only first word in string

I'm making a first project "Flashcards" for Kotlin course and faced with unexpected program behavior. Function "readln()" gets only first word in sting (separetad by space " "). What is a problem, what do you think?
Note: fun "reading" needed and for now I can realize it in this way only. Regular "readln()" have same problem.
Code below.
import java.util.*
var logText: String = ""
fun println(text: String = "") {
kotlin.io.println(text)
logText += text + "\n"
}
fun reading(): String {
val scan = Scanner(System.`in`)
val text = scan.next()
logText += text + "\n"
return text
}
fun adding(addCard: MutableMap<String, String>): MutableMap<String, String> {
val definition: String
println("The card:")
val card: String = reading()
if (card in addCard.keys) {
println("The card \"$card\" already exists.")
return addCard
} else {
println("The definition of the card:")
definition = reading()
if (definition in addCard.values) {
println("The definition \"$definition\" already exists.")
return addCard
}
}
addCard[card] = definition
println("The pair (\"${card}\":\"${definition}\") has been added.")
return addCard
}
fun main() {
val actionList = mutableListOf("add", "exit")
var action = ""
val cards = mutableMapOf<String, String>()
while (action != "exit") {
println("Input the action (add, remove, exit):")
action = reading()
if (action !in actionList) {
println("Wrong action!")
continue
} else {
when (action) {
"add" -> cards.putAll(adding(cards))
/* "remove" -> println("Not supported.")
"import" -> println("Not supported.")
"export" -> println("Not supported.")
"ask" -> println("Not supported.")
"log" -> println("Not supported.")
"hardest card" -> println("Not supported.")
"reset stats" -> println("Not supported.") */
}
}
}
println("Bye bye!")
}
You're not using readln() in your code, you're using a Scanner with the next() function and no delimiter set. So by default, that just splits on words:
import java.util.Scanner
fun main() {
val input = "here are some words"
val scanner = Scanner(input)
while(scanner.hasNext()) {
println(scanner.next())
}
}
here
are
some
words
If you use scanner.nextLine() then it'll read the whole line as a token, and that's basically the behaviour of readln() as well. If readln() is giving you individual words, you'll have to post the code that's doing that, because it'll be something else going on

How to simplify when expression in kotlin

I'd like to simplify this expression, especially "isDigit" and "isLetter" case. How to do it?
smoothInput.forEach { char ->
when {
char.isValidOperator() -> {
output.push(char)
}
char.isDigit() -> {
if (output.isNotEmpty() && output.last()!!.isNumeric()) output.addToLast(char)
else output.push(char)
}
char.isLetter() -> {
if (output.isNotEmpty() && output.last()!!.isValidVariableName()) output.addToLast(char)
else output.push(char)
}
else -> {
throw InvalidIdentifierException()
}
}
}
I think, that it isn't important, but it's much better to add code here than in comment
output is InputStack Type:
class InputStack : Stack<String> {
override val storage = mutableListOf<String>()
fun push(e: Char) = push(e.toString())
fun push(e: Operator) = push(e.toString())
fun addToLast(e: Char) {
storage[storage.size - 1] += e.toString()
}
}
Stack Interface:
interface Stack<T> {
val storage: MutableList<T>
fun asString(): String = buildString {
appendLine("----top----")
storage.asReversed().forEach {
appendLine(it)
}
appendLine("-----------")
}
fun push(element: T) = storage.add(element)
fun pop(): T {
if (storage.size == 0) throw EmptyStackException()
return storage.removeAt(storage.size - 1)
}
fun isEmpty(): Boolean = storage.isEmpty()
fun isNotEmpty(): Boolean = !isEmpty()
fun last(): T? = storage.lastOrNull()
fun forEach(action: (T) -> Unit) {
for (element in storage) action(element)
}
}
You can extract some common parts in the following way:
fun addCharToOutputConditionally(char: Char, output: InputStack, conditionOnLast: (String) -> Boolean) {
if (output.isNotEmpty() && conditionOnLast(output.last()!!)) output.addToLast(char)
else output.push(char)
}
smoothInput.forEach { char ->
when {
char.isValidOperator() -> {
output.push(char)
}
char.isDigit() -> {
addCharToOutputConditionally(char, output) {
it.isNumeric()
}
}
char.isLetter() -> {
addCharToOutputConditionally(char, output) {
it.isValidVariableName()
}
}
else -> {
throw InvalidIdentifierException()
}
}
}
However, in cases like this, I don't think it's usually worth spending the time to refactor it this way, considering that there's little to gain by doing so: the resulting code is even longer and arguably harder to read than the original one.
The new when expression:
smoothInput.forEach { char ->
when {
char.isValidOperator() -> output.push(char)
char.isDigit() -> output.addToLastConditionally(char) { it.isNumeric() }
char.isLetter() -> output.addToLastConditionally(char) { it.isValidVariableName() }
else -> throw InvalidIdentifierException()
}
}
I've change the addToLast function in InputStack for addToLastConditionally
fun addToLastConditionally(char: Char, condition: (String) -> Boolean) {
if (isNotEmpty() && condition(last()!!)) storage[storage.size - 1] += char.toString()
else push(char)
}

Chain kotlin flows depends on Result state

I'm looking for the most "clean" way to implement the following logic:
I have N methods, everyone returns Flow<Result<SOME_TYPE>> (type are different)
I want to chain these methods, so if 1 returns Result.Success, then call 2nd and so on.
The most obvious way to do it is:
methodA().map { methodAResult ->
when (methodAResult) {
is Result.Success -> {
methodB(methodAResult).map { methodBResult ->
when (methodBResult) {
is Result.Success -> {
methodC(methodAResult).map { methodCResult ->
when (methodCResult) {
is Result.Success -> TODO()
is Result.Failure -> TODO()
}
}
}
is Result.Failure -> TODO()
}
}
}
is Result.Failure -> TODO()
}
}
But it looks like a well-known "callback hell". Do u have any ideas how to avoid it?
I believe this could be flattened with transform operator:
methodA().transform { methodAResult ->
when (methodAResult) {
is Success -> methodB(methodAResult).collect { emit(it) }
is Failure -> TODO()
}
}.transform { methodBResult ->
when (methodBResult) {
is Success -> methodC(methodBResult).collect { emit(it) }
is Failure -> TODO()
}
}.transform { methodCResult ->
when (methodCResult) {
is Success -> TODO()
is Failure -> TODO()
}
}
A slight modification to the solution provided by Михаил Нафталь
methodA()
.flatMapMerge {
when (it) {
is Result.Success -> methodB(it)
is Result.Failure -> emptyFlow()
}
}.flatMapMerge {
when (it) {
is Result.Success -> methodC(it)
is Result.Failure -> emptyFlow()
}
}.collect {
when (it) {
is Result.Success -> TODO()
is Result.Failure -> TODO()
}
}
Merging the output of one flow to another flow is the goal of flatMap so using flatMap seems a little cleaner.
If this Result class has a map, fold, or getOrNull type method this could be cleaned up a bit more and the when blocks could be removed.
Also if you need to propagate the failure to collect then you could replace the calls to emptyFlow with a flow that just outputs the failure that you want.
Badly a flatMap method still doesn't exist.
But you can use mapCatching :
methodA
.mapCatching { a -> methodB(a).getOrThrow() }
.mapCatching { b -> methodC(b).getOrThrow() }
Or make your own flatMap extension function :
fun <T, R> Result<T>.flatMap(block: (T) -> (Result<R>)): Result<R> {
return this.mapCatching {
block(it).getOrThrow()
}
}
methodA
.flatMap { a -> methodB(a) }
.flatMap { b -> methodC(b) }
I believe that in this use case you should probably use suspend functions and compose them using await().
Errors should be passed through exceptions as described here.

how to insert items by title and subitem, using getItemViewType

How to insert an arraylist in recyclingeview by sorting before inserting. Just below the example
[PRATO FEITO ] -> TITLE
[ COM ACOMPANHAMENTO] -> SUB ITEM
[ COM FEIJOADA ] -> SUB ITEM
[ ]
[ESPETINHO ] -> TITLE
[ COM BIFÉ ] -> SUB ITEM
[ COM ACOMPANHAMENTO] -> SUB ITEM
See how the result is doing. It's running out of order
[PRATO FEITO ] -> TITLE
[ESPETINHO ] -> TITLE
[ COM BIFÉ ] -> SUB ITEM
[ COM ACOMPANHAMENTO] -> SUB ITEM
[ COM FEIJOADA ] -> SUB ITEM
[ COM MANDIOCA ] -> SUB ITE
My Code List
class Lista_Itens_Mesa : AppCompatActivity() {
val client by lazy {
Api.create()
}
var disposable: Disposable? = null
private lateinit var recycleview: RecyclerView
private lateinit var viewadapter1: RecyclerView.Adapter<*>
private lateinit var viewManager: RecyclerView.LayoutManager
private lateinit var db: AppBancoDados
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.lista__itens__mesa_pratos) //lista__itens__mesa_pratos
val numbercolumns = 1
recyclerViewItens_Mesa.layoutManager = LinearLayoutManager(this)
recyclerViewItens_Mesa.addItemDecoration(DividerItemDecoration(this, OrientationHelper.HORIZONTAL))
BuscarTestando()
}
fun BuscarTestando(){
disposable = client.list_pratos(idmesas)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
val array = ArrayList<Pr_atos>()
result[0].pratos.forEach {
array.add(Pr_atos(it?.dESCRICAOPRODUTO, null, it?.POST_TYPE))
}
result[1].complementos.forEach {
array.add(Pr_atos(null, it?.cOMPLEMENTODESCRICAO, it?.POST_TYPE))
}
viewadapter1 = Itens_Mesa_Adapter(array)
recyclerViewItens_Mesa.adapter = viewadapter1
},
{ error ->
println(" Erro não esperado! " + error.printStackTrace())
})
}
}
My Adapter Custom
private const val POST_TYPE_PRATOS : Int = 1
private const val POST_TYPE_COMPLEMENTOS: Int = 0
class Itens_Mesa_Adapter( var itens_mesa:List<Pr_atos>): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if(viewType == POST_TYPE_PRATOS){
val view_pratos = LayoutInflater.from(parent.context).inflate(R.layout.itens__mesa_pratos,parent,false)
return ViewHolderPratos(view_pratos)
}else(viewType == POST_TYPE_COMPLEMENTOS)
val view_complementos = LayoutInflater.from(parent.context).inflate(R.layout.itens_mesa_complemento,parent,false)
return ViewHolderComplementos(view_complementos)
}
override fun getItemCount(): Int {
return itens_mesa.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if(getItemViewType(position) == POST_TYPE_PRATOS){
(holder as ViewHolderPratos).bind(itens_mesa[position])
}else{
(holder as ViewHolderComplementos).bind(itens_mesa[position])
}
}
override fun getItemViewType(position: Int): Int {
return if(itens_mesa[position].post_type == 1){
POST_TYPE_PRATOS
}else{
POST_TYPE_COMPLEMENTOS
}
}
class ViewHolderPratos(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind(itens : Pr_atos){
itemView.TextViewPedidoName.text = itens.produtoname
}
}
class ViewHolderComplementos(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind(itens: Pr_atos){
itemView.textViewcomplemento_pedido.text = itens.complementoname
}
}
}

Kotlin: Remove list in Recycerview using AlertDialog button

I hope to remove items in Recyclerview by AlertDialog button.
Do you have idea?
Adapter & Viewholer
class ProjectsRecyclerAdapter(val list:List<ProjectListDataModel>, var clickListner: OnProjectListClickListner) : RecyclerView.Adapter<ProjectRecyclerViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProjectRecyclerViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_cardview, parent, false)
return ProjectRecyclerViewHolder(
view
)
}
override fun getItemCount(): Int {
return list.count()
}
override fun onBindViewHolder(holder: ProjectRecyclerViewHolder, position: Int) {
holder.initialize(list[position], clickListner)
}
}
class ProjectRecyclerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun initialize(item: ProjectListDataModel, action: OnProjectListClickListner) {
itemView.projectNameText.text = item.name
itemView.modifiedTimeText.text = item.modTime
itemView.descriptionText.text = item.description
itemView.setOnClickListener {
action.onItemClick(item, adapterPosition)
}
}
}
interface OnProjectListClickListner {
fun onItemClick(item: ProjectListDataModel, position: Int)
}
And here is Main Activity
class LocalProjectListActivity :
AppCompatActivity(),
OnProjectListClickListner,
NavigationView.OnNavigationItemSelectedListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main)
val localProjectList = listOf(
ProjectListDataModel(
"Project A",
"Sun, 01/DEC/2020, 22:23GMT",
"Testing 1",
"AAAA",
"A, A",
8,
232
),
ProjectListDataModel(
"Project B",
"Sun, 01/JUL/2020, 10:23GMT",
"Testing 2",
"BBBB",
"B, B",
6,
354
),
ProjectListDataModel(
"Project C",
"Sun, 11/MAR/2020, 08:31GMT",
"Testing 3",
"CCCC",
"C,C",
15,
632
)
)
val adapter = ProjectsRecyclerAdapter(localProjectList, this)
projectNameText.adapter = adapter
projectNameText.setHasFixedSize(true)
projectNameText.layoutManager = LinearLayoutManager(this)
}
override fun onItemClick(item: ProjectListDataModel, position: Int) {
val builder = AlertDialog.Builder(this)
val ad = builder.create()
val dialogView = layoutInflater.inflate(R.layout.project_onclick, null)
dialogView.projectNameText.text = item.name
dialogView.modifiedTimeText.text = item.modTime
dialogView.descriptionText.text = item.description
dialogView.fileNumberText.text = item.fileNumber.toString() + "EA"
dialogView.fileSizeText.text = item.fileSize.toString() + "MB"
dialogView.scopeText.text = item.scope
dialogView.userIdText.text = item.resposible
ad.setView(dialogView)
ad.show()
//when click Load button
dialogView.loadButton.setOnClickListener {
ad.dismiss()
val dialog = AlertDialog.Builder(this)
dialog.setTitle("Question1")
dialog.setMessage("You want to go to the project?")
dialog.setPositiveButton("Yes", DialogInterface.OnClickListener { _, _ ->
})
dialog.setNegativeButton("No", DialogInterface.OnClickListener { _, _ ->
val fileFilterIntent = Intent(this, ProjectFileActivity::class.java)
startActivity(fileFilterIntent)
})
dialog.setNeutralButton("Cancel", DialogInterface.OnClickListener {dialog, _ ->
dialog.dismiss()
})
//if(isMarker == true) {
dialog.show()
//} else {}
}
dialogView.deleteButton.setOnClickListener {
ad.dismiss()
val dialog = AlertDialog.Builder(this)
dialog.setTitle("Delet project list")
dialog.setMessage("You want to delete project?")
dialog.setPositiveButton("Yes", DialogInterface.OnClickListener { _, _ ->
**//Delete items in RecyclerView**
})
dialog.setNeutralButton("Cancel", null)
dialog.show()
}
dialogView.cancleButton.setOnClickListener { ad.dismiss() }
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.remoteProjects -> {
val serverProjIntent = Intent(this, ServerProjectListActivity::class.java)
startActivity(serverProjIntent)
}
R.id.settings -> {
Toast.makeText(this, "Go to Settings", Toast.LENGTH_SHORT).show()
}
}
mainLocal.closeDrawer(GravityCompat.START)
return true
}
}
I posted all the code like that.
I can get the lists using Recyclerview, and made the AlertDialog But I don't know how to delete the items using the "Yes" button in AlertDialog.
Please give me some advises.
You'll need to have a reference to the list and the adapter outside of onCreate
private val localProjectList = mutableListOf(
ProjectListDataModel(
"Project A",
"Sun, 01/DEC/2020, 22:23GMT",
"Testing 1",
"AAAA",
"A, A",
8,
232
),
ProjectListDataModel(
"Project B",
"Sun, 01/JUL/2020, 10:23GMT",
"Testing 2",
"BBBB",
"B, B",
6,
354
),
ProjectListDataModel(
"Project C",
"Sun, 11/MAR/2020, 08:31GMT",
"Testing 3",
"CCCC",
"C,C",
15,
632
)
)
private lateinit var adapter: ProjectsRecyclerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main)
adapter = ProjectsRecyclerAdapter(localProjectList, this)
projectNameText.adapter = adapter
projectNameText.setHasFixedSize(true)
projectNameText.layoutManager = LinearLayoutManager(this)
}
And then delete the list and notify the adapter
private fun deleteItemsAndNotifyAdapter() {
localProjectList.clear()
adapter.notifyDataSetChanged()
}
You can delete and notify adapter in you dialog:
private var alertDialog: AlertDialog? = null
private fun showDeleteDialog() {
val builder = AlertDialog.Builder(activity)
builder.run {
setTitle("Delete project list")
setMessage("You want to delete project?")
setPositiveButton("Yes") { _, _ ->
deleteItemsAndNotifyAdapter()
}
setNegativeButton("Cancel") { _, _ ->
}
}
alertDialog = builder.create()
alertDialog?.show
}