Is there is any way to stop for loop in Kotlin in middle of process without using a break ?
Example :
for (item in items) {
for (item2 in arrRealmTemp ) {
if (item.MKT == item2.MKT && item.UPC == item2.UPC ) {
// I want to stop this (the inner for only ) here
}
}
}
You can use label in following way.
loop# for (i in 1..100) {
for (j in 1..100) {
if (...) break#loop
}
}
In Kotlin you can use labels and continue to be sure to continue with the next iteration of a for loop:
loop# for (item in items) {
for (item2 in arrRealmTemp ) {
if (item.MKT == item2.MKT && item.UPC == item2.UPC ) {
continue#loop
}
}
}
Related
In the following code, I have two parts A and B. I need to extract part B as a common part for more of my pages.
But it contains item, I cannot extract item, but item must be included in the if judgment because paging3 will scroll to the top for extra item.
Is there a way to extract item?
LazyColumn(Modifier.fillMaxSize()) {
// Part A
items(pagingItems) { wind ->
WindRow(navController, wind!!)
}
val refresh = pagingItems.loadState.refresh
val append = pagingItems.loadState.append
// Part B
if (refresh is LoadState.NotLoading && append is LoadState.NotLoading) {
if (pagingItems.itemCount == 0) {
item {
PosterCompose() {
navController.navigate("blowWind")
}
}
}
} else {
item {
LoadStateView(path = FOLLOW_WIND_LIST, refresh = refresh, append = append) {
pagingItems.retry()
}
}
}
}
I solved this problem
fun <T : Any> LazyListScope.newItems(pagingItems: LazyPagingItems<T>) {
val refresh = pagingItems.loadState.refresh
val append = pagingItems.loadState.append
if (refresh is LoadState.NotLoading && append is LoadState.NotLoading) {
if (pagingItems.itemCount == 0) {
item {
PosterCompose() {
}
}
}
}else{
item {
LoadStateView(path = FOLLOW_WIND_LIST, refresh = refresh, append = append) {
pagingItems.retry()
}
}
}
}
This is how to use
LazyColumn(Modifier.fillMaxSize()) {
items(pagingItems) { wind ->
WindRow(navController, wind!!)
}
newItems(pagingItems)
}
I am trying to make TicTacToe Application, I have already implemented the "player Vs player" part but I am having trouble with implementing the player Vs computer. I have a function called playgame. For update_player I am doing manually and for update computer, I am doing it using random, and I think this is causing the issue as I am checking if my boardStatus is already filled, if it's filled I am calling my function again. I read online that all the calculation should be done on thread, I tried implementing it but I think I am doing it wrong. Please Help!
Here's my code for reference:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import kotlinx.android.synthetic.main.activity_computer.*
import java.util.*
class ComputerActivity : AppCompatActivity() {
var player = true
var turnCount = 0
var boardStatus = Array(3) { IntArray(3) }
lateinit var board: Array<Array<Button>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_computer)
board = arrayOf(
arrayOf(First, Second, Third),
arrayOf(Fourth, Fifth, Sixth),
arrayOf(Seventh, Eighth, Ninth)
)
playGame()
}
private fun playGame(){
Status.text = "Player's Turn"
for (i in 0..2) {
var flag: Boolean = false
for (j in 0..2) {
if (player) {
Status.text = "Player's Turn"
update_player(player)
Thread.sleep(2000)
player = false
turnCount++
checkWinner()
if (turnCount == 9) {
Status.text = "Game Draw"
flag = true;
}
} else {
Status.text = "Computer's Turn"
update_computer(player)
Thread.sleep(2000)
player = true
turnCount++
checkWinner()
if (turnCount == 9){
Status.text = "Game Draw"
flag = true
}
}
}
if(flag == true)
break
}
changeBoard()
resetBtn.setOnClickListener{
player = true;
turnCount = 0
changeBoard()
}
}
private fun update_player(player:Boolean){
for(i in board){
for(button in i){
button.setOnClickListener{
when(it.id){
R.id.First->{
updateBoardStatus(row = 0, column = 0,player)
}
R.id.Second->{
updateBoardStatus(row = 0, column = 1,player)
}
R.id.Third->{
updateBoardStatus(row = 0, column = 2,player)
}
R.id.Fourth->{
updateBoardStatus(row = 1, column = 0,player)
}
R.id.Fifth->{
updateBoardStatus(row = 1, column = 1,player)
}
R.id.Sixth->{
updateBoardStatus(row = 1, column = 2,player)
}
R.id.Seventh->{
updateBoardStatus(row = 2, column = 0,player)
}
R.id.Eighth->{
updateBoardStatus(row = 2, column = 1,player)
}
R.id.Ninth->{
updateBoardStatus(row = 2, column = 2,player)
}
}
}
}
}
}
private fun update_computer(player:Boolean){
var row:Int = 0
var column:Int = 0
Thread {
row = (0..2).random()
column = (0..2).random()
}.start()
if(boardStatus[row][column] == 0 || boardStatus[row][column]==1)
update_computer(player)
else
updateBoardStatus(row, column, player)
}
private fun updateBoardStatus(row:Int, column:Int, player:Boolean){
val text = if (player) "X" else "0"
val value = if (player) 1 else 0
board[row][column].apply {
isEnabled = false
setText(text)
}
boardStatus[row][column] = value
}
private fun checkWinner(){
//Horizontal --- rows
for (i in 0..2) {
if (boardStatus[i][0] == boardStatus[i][1] && boardStatus[i][0] == boardStatus[i][2]) {
if (boardStatus[i][0] == 1) {
result("Player Won!!")
break
} else if (boardStatus[i][0] == 0) {
result("Computer Won")
break
}
}
}
//Vertical --- columns
for (i in 0..2) {
if (boardStatus[0][i] == boardStatus[1][i] && boardStatus[0][i] == boardStatus[2][i]) {
if (boardStatus[0][i] == 1) {
result("Player Won!!")
break
} else if (boardStatus[0][i] == 0) {
result("Computer Won!!")
break
}
}
}
//First diagonal
if (boardStatus[0][0] == boardStatus[1][1] && boardStatus[0][0] == boardStatus[2][2]) {
if (boardStatus[0][0] == 1) {
result("Player Won!!")
} else if (boardStatus[0][0] == 0) {
result("Computer won!!")
}
}
//Second diagonal
if (boardStatus[0][2] == boardStatus[1][1] && boardStatus[0][2] == boardStatus[2][0]) {
if (boardStatus[0][2] == 1) {
result("Player Won!!")
} else if (boardStatus[0][2] == 0) {
result("Computer Won!!")
}
}
}
private fun result(res:String){
Status.text = res
if(res.contains("Won")){
disableButton()
}
else{
}
}
private fun disableButton(){
for(i in board){
for(button in i){
button.isEnabled = false
}
}
}
private fun changeBoard(){
for (i in 0..2) {
for (j in 0..2) {
boardStatus[i][j] = -1
}
}
for (i in board) {
for (button in i) {
button.isEnabled = true
button.text = ""
}
}
}
}
Your code is trying to put the whole sequence of actions of the game in a function that is called once and then expects player button-presses to happen internally. Button listeners will fire some time in the future, after the function already returns. You need to think in terms of there being a function that is called each time a button is pressed to do the next stage of the game.
To fix this:
Remove the playGame() function.
Remove the player parameter from update_player() since it's always true. And change the function name to initializeButtons. Call it once in onCreate(). You only have to add listeners to the buttons one time and they will work repeatedly.
Also remove the player parameter from update_computer() for the same reason as above. And remove the threading so it looks like:
private fun update_computer() {
val row = (0..2).random()
val column = (0..2).random()
if (boardStatus[row][column] == 0 || boardStatus[row][column] == 1)
update_computer()
else
updateBoardStatus(row, column, player)
}
Then at the end of the updateBoardStatus function call checkWinner(). checkWinner() should return a Boolean, so in updateBoardStatus(), if no win condition has been found and player is true, it should call update_computer().
So what you have now instead of trying to run the game from one function, you set up button listeners one time to start the game. When a button is pressed, it takes the player turn, which then triggers updateBoardStatus, which then triggers the computer turn, which then triggers updateBoardStatus again, and then does nothing if no one won. All of that happens synchronously/instantly on the main thread, so now the game is back to waiting for a button press from the user to repeat the sequence of events.
Also, the status text view has limited usefulness. Since the computer takes its turns instantly, it's not possible to ever see the words "Computer's turn". If you want to do that, you'll have to create an artificial delay, so you would have to disable all the buttons and then do something like call postRunnable({ startPlayerTurn() }, 1000L), where the startPlayerTurn() re-enables the appropriate buttons and makes it say, "Player turn" again.
I'm currently playing around with the new Jetpack compose UI toolkit and I like it a lot. One thing I could not figure out is how to use stickyHeaders in a LazyColumn which is populated by the paging library. The non-paging example from the documentation is:
val grouped = contacts.groupBy { it.firstName[0] }
fun ContactsList(grouped: Map<Char, List<Contact>>) {
LazyColumn {
grouped.forEach { (initial, contactsForInitial) ->
stickyHeader {
CharacterHeader(initial)
}
items(contactsForInitial) { contact ->
ContactListItem(contact)
}
}
}
}
Since I'm using the paging library I cannot use the groupedBy so I tried to use the insertSeparators function on PagingData and insert/create the headers myself like this (please ignore the legacy Date code, it's just for testing):
// On my flow
.insertSeparators { before, after ->
when {
before == null -> ListItem.HeaderItem(after?.workout?.time ?: 0)
after == null -> ListItem.HeaderItem(before.workout.time)
(Date(before.workout.time).day != Date(after.workout.time).day) ->
ListItem.HeaderItem(before.workout.time)
// Return null to avoid adding a separator between two items.
else -> null
}
}
// In my composeable
LazyColumn {
items(workoutItems) {
when(it) {
is ListItem.HeaderItem -> this#LazyColumn.stickyHeader { Header(it) }
is ListItem.SongItem -> WorkoutItem(it)
}
}
}
But this produces a list of all my items and the header items are appended at the end. Any ideas what is the right way to use the stickyHeader function when using the paging library?
I got it to work by looking into the source code of the items function: You must not call stickyHeader within the items function. No need to modify the PagingData flow at all. Just use peek to get the next item without triggering a reload and then layout it:
LazyColumn {
val itemCount = workoutItems.itemCount
var lastWorkout: Workout? = null
for(index in 0 until itemCount) {
val workout = workoutItems.peek(index)
if(lastWorkout?.time != workout?.time) stickyHeader { Header(workout) }
item { WorkoutItem(workoutItems.getAsState(index).value) } // triggers reload
lastWorkout = workout
}
}
I believe the issue in your code was that you were calling this#LazyColumn from inside an LazyItemScope.
I experimented too with insertSeparators and reached this working LazyColumn code:
LazyColumn {
for (index in 0 until photos.itemCount) {
when (val peekData = photos.peek(index)) {
is String? -> stickyHeader {
Text(
text = (photos.getAsState(index).value as? String).orEmpty(),
)
}
is Photo? -> item(key = { peekData?.id }) {
val photo = photos.getAsState(index).value as? Photo
...
}
}
}
}
when I add multiple loop to retrieve data from firebase only the first loop works, why is that happening and how can I make all the loops work
val children = dataSnapshot!!.children
children.forEach {
if( it.child("Date").getValue().toString().equals(Data.date) && it.child("Time").getValue().toString().equals(Data.time) && it.child("location").getValue().toString().equals(items.get(0).toString())){
//
}
}
children.forEach {
if( it.child("Date").getValue().toString().equals(Data.date) && it.child("Time").getValue().toString().equals(Data.time) && it.child("location").getValue().toString().equals(items.get(1).toString())){
//
}
}
children.forEach {
if( it.child("Date").getValue().toString().equals(Data.date) && it.child("Time").getValue().toString().equals(Data.time) && it.child("location").getValue().toString().equals(items.get(2).toString())){
//
}
}
At first glance the 3 loops look identical, but they differ at the last 3d of the condition. Each one checks this:
it.child("location").getValue().toString().equals(items.get(x).toString())
where x is 0, 1 or 2.
So they are not the same as to be surprised why the last 2 don't work as you say.
Maybe the condition works only for:
items.get(0).toString()
Anyway you can simplify your code and add some debugging code so to be sure what's happening:
(0..2).forEach { i ->
children.forEach {
val dateOk = it.child("Date").getValue().toString().equals(Data.date)
val timeOk = it.child("Time").getValue().toString().equals(Data.time)
val itemOk = it.child("location").getValue().toString().equals(items.get(i).toString())
Log.d("Loop", "at $i: dateOk = $dateOk, timeOk = $timeOk, itemOk = $itemOk")
if( dateOk && timeOk && itemOk){
when (i) {
0 -> //
1 -> //
2 -> //
}
}
}
}
is anyone facing this problem.
break or continue jump across class boundary kotlin
this problem appears when i am going to use break or continue. inside lambda with receiver i create 'letIn'
lambda with receiver code
fun letIn(componentName: String?, values: List<LifeService.Value?>?,
body: (String, List<LifeService.Value?>) -> Unit) {
if (!TextUtils.isEmpty(componentName) && (values != null && values.isNotEmpty())) {
body(componentName!!, values)
}
}
this sample code for it.
for (option in 0 until optionsSize) {
val component = optionsGroup?.options?.get(option)
component?.let {
with(component) {
letIn(presentation, values, { componentName, values ->
if (componentName == LifeComponentViewType.CHECKBOX) {
letIn(transformCheckBoxValues(optionsGroup), { data ->
dataSource?.push(componentName, ComponentDataCheckBoxCollection(name, data))
view.buildComponent(componentName)
// break or continue didnt work
})
} else {
dataSource?.push(componentName, ComponentDataCollection(name, values))
view.buildComponent(componentName)
}
})
}
}
}
because above code didnt work so i use imperative way.
for (option in 0 until optionsSize) {
val component = optionsGroup?.options?.get(option)
if (component != null) {
val presentation: String? = component.presentation
val values = component.values
if (!TextUtils.isEmpty(presentation)) {
if (presentation == LifeComponentViewType.CHECKBOX) {
val data = transformCheckBoxValues(optionsGroup)
if (data.isNotEmpty()) {
dataSource?.push(presentation, ComponentDataCheckBoxCollection(optionsGroup.name, data))
view.buildComponent(presentation)
return
}
} else {
dataSource?.push(presentation!!, ComponentDataCollection(component.name, values))
view.buildComponent(presentation!!)
}
} else {
return
}
}
}
does anyone have suggestions?
UPDATE
i've been fix this issue by inlining high order function.
(Other coding errors aside) You are seeing the error because inside your lambda, you cannot use break or continue to jump out of the lambda to the nearest loop. Instead, you can use a qualified return to jump out of the lambda to a label.
Referring to the language reference
The return-expression returns from the nearest enclosing function, i.e. foo. (Note that such non-local returns are supported only for lambda expressions passed to inline functions.) If we need to return from a lambda expression, we have to label it and qualify the return:
(Emphasis mine)
Your second example shows that you want your lambdas to do a non-local return from the enclosing function. Therefore, you do not need to qualify your return, but your function letIn must be declared inline (else you can only do a local, qualified return).
inline fun letIn(componentName: String?, values: List<LifeService.Value?>?,
body: (String, List<LifeService.Value?>) -> Unit) {
if (!TextUtils.isEmpty(componentName) && (values != null && values.isNotEmpty())) {
body(componentName!!, values)
}
}
... or if you want it to have receivers...
inline fun String?.letIn(values: List<LifeService.Value?>?,
body: String.(List<LifeService.Value?>) -> Unit) {
if (!TextUtils.isEmpty(this) && (values != null && values.isNotEmpty())) {
this!!.body(values)
}
}
When you declare letIn as inline, then you can place return in your lambdas without the compiler complaining. Your function would not need to be inline if your lambdas are only doing local returns, but it would need to have a qualified return (for example return#letIn).
Your first example would then look like this...
for (option in 0 until optionsSize) {
val component = optionsGroup?.options?.get(option)
component?.let {
with(component) {
presentation.letIn(values, { values ->
if (this == LifeComponentViewType.CHECKBOX) {
this.letIn(transformCheckBoxValues(optionsGroup), { data ->
dataSource?.push(this, ComponentDataCheckBoxCollection(this, data))
view.buildComponent(this)
return //returns from function
})
} else {
dataSource?.push(this, ComponentDataCollection(name, values))
view.buildComponent(this)
return //returns from function
}
})
}
}
}
Lastly, note that if you wanted to jump out of the lambda early, but continue an outer loop as in:
fun test1() {
val list = listOf("a", "b", "c")
val optionsSize = 2
for(i in 0..optionsSize) loop# {
println("calliing list.forEach")
list.forEach lit# {
if(it == "a") return#lit
if(it == "c") return#loop
println(it)
}
}
}
It won't work. Intelli-sense doesn't complain about it, but the compiler throws an internal error. But you can convert the outer loop to a lambda, and it does work...
fun test() {
val list = listOf("a", "b", "c")
val optionsSize = 2
(0..optionsSize).forEach() loop# {
println("calliing list.forEach")
list.forEach lit# {
if(it == "a") return#lit
if(it == "c") return#loop
println(it)
}
}
}
Again, this only works if the function to which the lambda is passed is declared inline (like forEach is declared inline).