How can I write do noting in Kotlin? - kotlin

ESelect is a enum structure. I hope it will do noting when it is ESelect.NoAction in the following code.
It will cause compile error if I write ; after ESelect.NoAction ->, how can I fix it?
Code
aHomeViewModel.selectAction.observe(mLifecycleOwner, {
when(it) {
ESelect.SelectAll - > binding.chSelect.isChecked = true
ESelect.UnselectAll - > binding.chSelect.isChecked = false
ESelect.NoAction - > ; //It will do nothing
}
})
enum class ESelect {
SelectAll,
UnselectAll,
NoAction
}

You could return Unit (which is like void in Java). The code will look like:
aHomeViewModel.selectAction.observe(mLifecycleOwner, {
when(it) {
ESelect.SelectAll -> binding.chSelect.isChecked = true
ESelect.UnselectAll -> binding.chSelect.isChecked = false
ESelect.NoAction -> Unit
}
})
See the Docu: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/

You could use when as an expression instead of a statement, and for the NoAction case assign the existing value:
binding.chSelect.isChecked = when (it) {
ESelect.SelectAll -> true
ESelect.UnselectAll -> false
ESelect.NoAction -> binding.chSelect.isChecked
}
Or use if:
if (it == ESelect.SelectAll) {
binding.chSelect.isChecked = true
} else if (it == ESelect.UnselectAll) {
binding.chSelect.isChecked = false
}

Related

State flow Android Kotlin

I have a god view model for every thing I know this is wrong
but I am just experimenting with Flow
I have these two State flow variables in view model
private val _currentRestroMenu = MutableStateFlow<State<Menu>>(State.loading())
private val _userCart = MutableStateFlow(CustomerCart())
val currentRestroMenu: StateFlow<State<Menu>> = _currentRestroMenu
val userCart: StateFlow<CustomerCart> = _userCart
Below functions get data from server and update above state flow
private fun getRestroMenuFromCloudAndUpdateData(restroId: String) = viewModelScope.launch {
fireStoreRepository.getRestroMenu(restroId).collect { state ->
when (state) {
is State.Success -> {
_currentRestroMenu.value = State.success(state.data)
dataHolderMenuOnSearch = state.data
if (!viewedRestroMenu.contains(state.data)) {
viewedRestroMenu.add(state.data)
}
}
is State.Failed -> {
_currentRestroMenu.value = State.failed(state.message)
}
is State.Loading -> {
_currentRestroMenu.value = State.loading()
}
}
}
}
private fun getCart() = viewModelScope.launch(Dispatchers.IO) {
if (currentCart.cartEmpty) {
fireStoreRepository.getUserCartInfoFromCloud(dataStoreRepository.readFileDataStoreValue.first().savedUserId)
.collect { cartState ->
when (cartState) {
is State.Success -> {
_userCart.update {
it.copy(
cartId = cartState.data.cartId,
cartEmpty = cartState.data.cartEmpty,
cartItem = cartState.data.getCartItem(),
restroId = cartState.data.restroId,
cartTotalAmount = cartState.data.cartTotalAmount,
cartAddressId = cartState.data.cartAddressId,
cartDeliveryTime = cartState.data.cartDeliveryTime,
cartCookingInstructions = cartState.data.cartCookingInstructions,
cartAppliedOfferId = cartState.data.cartAppliedOfferId,
deliveryPartnerTipAmount = cartState.data.deliveryPartnerTipAmount,
cartDeliveryCharge = cartState.data.cartDeliveryCharge,
cartTax = cartState.data.cartTax,
deliveryInstructionId = cartState.data.deliveryInstructionId,
foodHandlingCharge = cartState.data.foodHandlingCharge,
cartNumberOfItems = cartState.data.cartNumberOfItems,
cartRestroName = cartState.data.cartRestroName
)
}
currentCart = cartState.data
}
is State.Failed -> {
if (cartState.message == "Result null") {
Log.d(
ContentValues.TAG,
"getCartFromCloud: No cart details found in cloud creating new cart"
)
_userCart.update {
it.copy(
cartId = dataStoreRepository.readFileDataStoreValue.first().savedUserId,
cartEmpty = true
)
}
currentCart = CustomerCart(
cartId = dataStoreRepository.readFileDataStoreValue.first().savedUserId,
cartEmpty = true
)
}
}
is State.Loading -> {
Log.d(ContentValues.TAG, "getCartFromCloud: Loading")
}
}
}
} else {
_userCart.value = currentCart
Log.d(ContentValues.TAG, "getCart: $currentCart ")
}
}
I am collecting these state flow from different fragments
every thing works fine except one fragment
here is the code
in on create method
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
godCustomerViewModel.currentRestroMenu.collectLatest { menuState ->
Log.d(TAG, "currentRestroMenu ::: mENUSELECT FIRED: ")
when (menuState) {
is State.Success -> {
restroMenu = menuState.data
binding.recyclerView2.hideShimmer()
getCartDetails(restroMenu)
}
is State.Failed -> {
Log.d(TAG, "currentRestroMenu: ")
}
is State.Loading -> {
binding.recyclerView2.showShimmer()
}
}
}
}
}
private fun getCartDetails(restroMenu: Menu) = viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
godCustomerViewModel.userCart.collectLatest {
if (it.restroId == restroMenu.restroId) {
categoryAdapterRestroDetails.setData(
restroMenu.menuCategories,
it.getCartItem()
)
} else {
categoryAdapterRestroDetails.setData(
restroMenu.menuCategories,
ArrayList()
)
}
}
}
}
I am passing the two collected values to adapter (retro menu and item in cart )
when the fragment is loaded for the first time everything works fine
I have add dish to cart function which updates the value of user cart
fun addDishToCart(dish: Dish) = viewModelScope.launch {
Log.d(ContentValues.TAG, "addDishToCart: view model invoked")
if (currentCart.checkIfCartBelongsToThisRestro(dish.dishRestroId)) {
currentCart.addDishToCart(dish).collect {
Log.d(ContentValues.TAG, "addDishToCartcollect: $currentCart")
_userCart.update {
it.copy(
cartEmpty = currentCart.cartEmpty,
cartItem = currentCart.getCartItem(),
restroId = currentCart.restroId,
cartTotalAmount = currentCart.cartTotalAmount,
cartNumberOfItems = currentCart.cartNumberOfItems,
)
}
}
} else {
// restro Conflict
Log.d(ContentValues.TAG, "addDishToCart: $currentCart")
_restroConflict.value = CartConflict(true, currentCart.cartRestroName, dish)
}
Log.d(ContentValues.TAG, "addDishToCart current cart: ${currentCart.getCartItem()}")
Log.d(ContentValues.TAG, "addDishToCart: user Cart : ${_userCart.value.getCartItem()} ")
}
Which also work fine initially
I also have a button to filter menu to veg non veg
fun filterMenuForVeg(value: Boolean, showAll: Boolean) = viewModelScope.launch {
if (!showAll) {
Log.d(ContentValues.TAG, "filterMenuForVeg: Entered veg :$value")
var filteredMenu = Menu()
filteredMenu.restroId = dataHolderMenuOnSearch.restroId
for (menuCategory in dataHolderMenuOnSearch.menuCategories) {
Log.d(ContentValues.TAG, "filterMenuForVeg: $dataHolderMenuOnSearch ")
for (dish in menuCategory.dishes) {
if (dish.dishVeg == value) {
Log.d(ContentValues.TAG, "found dish with veg $value: ")
var categoryAlreadySaved = false
filteredMenu.menuCategories.filter {
categoryAlreadySaved = it.categoryId == menuCategory.categoryId
true
}
if (!categoryAlreadySaved) {
Log.d(ContentValues.TAG, "menu category not found in filtered list ")
val menuCategoryToAdd = MenuCategories()
menuCategoryToAdd.menuCategoryName = menuCategory.menuCategoryName
menuCategoryToAdd.categoryId = menuCategory.categoryId
menuCategoryToAdd.restroId = menuCategory.restroId
menuCategoryToAdd.dishes.add(dish)
filteredMenu.menuCategories.add(menuCategoryToAdd)
} else {
Log.d(ContentValues.TAG, "menu category found in filtered list ")
filteredMenu.menuCategories.find {
if (it.categoryId == menuCategory.categoryId) {
it.restroId = menuCategory.restroId
it.dishes.add(dish)
}
true
}
}
}
}
}
Log.d(ContentValues.TAG, "filterMenuForVeg : $filteredMenu ")
_currentRestroMenu.value = State.success(filteredMenu)
} else {
// set to all data
_currentRestroMenu.value = State.success(dataHolderMenuOnSearch)
}
When I filter dish for veg or non veg then add dish to cart (Which only changes userCart State flow) the place where I am collecting these state flow
get fired twice
so set data to adapter is getting called twice
What Iam doing wrong
Could you collect the items with onEach instead of collectLatest? It would solve your problem probably.

How can I convert for loop to list functions in Kotlin

I'm reading The Big Nerd Ranch Guide and here is a question that wants you to use the list functions instead of for loop
var count = 0
for (answer in answerList) {
if (!answer.isCorrect) {
answer.isEnabled = false
answer.isSelected = false // deselect when answer is disabled
count++
if (count == 2) {
break
}
}
}
here is my solution, but I don't know how to deal with count
var count = 0
answerList
.filter { !it.isCorrect }
.forEach {
it.isSelected = false
it.isSelected = false
count++
}
.takeIf { count == 2 }
If you want to perform the actions for the first two (at most) items that are "not correct", you could do this, with take:
answerList
.filter { !it.isCorrect }
.take(2)
.forEach {
it.isEnabled = false
it.isSelected = false
}

Better way to replace nested if else in Kotlin

Is there any better way to replace below if else to more cleaner in Kotlin. I tried to replace with when statement but i couldn't match the logic.
if (!reached)
{
if (!info)
{
d.sinfo = extractinfo()
}
else
{
parserMessage("print something")
return d
}
info = true
}
else
{
if (d.media.isEmpty()){
parserMessage("print something")
return d
}
else{
if (d.media.at(d.media.size() - 1).media_information.isEmpty())
{d.media[d.media.size() - 1].minfo = extractinfo()}
else{
parserMessage("print something")
return d
}
}
}
Unless the code you have left out have some weird side effects, this code should be semantically equal:
when {
!reached && !info -> {
d.sinfo = extractinfo()
info = true
}
!reached && info -> {
parserMessage("print something")
return d
}
d.media.isEmpty() -> {
parserMessage("print something")
return d
}
d.media.at(d.media.size() - 1).media_information.isEmpty() -> {
d.media[d.media.size() - 1].minfo = extractinfo()
}
else -> {
parserMessage("print something")
return d
}
}
However, to say this, I had to fill in the gaps in the code you have presented myself, so I can't state this very confidently. It really helps your chances of getting a good answer if the code you want help with is runnable/understandable as presented.
By the way. This refactoring was partly done by pasting the code into IntelliJ and hitting Alt+Enter and choosing "Replace 'if' with 'when'" and "Flatten when"

Return Option inside Loop

The program aims to use a loop to check if the index of a iterator variable meets certain criteria (i.g., index == 3). If find the desired index, return Some(123), else return None.
fn main() {
fn foo() -> Option<i32> {
let mut x = 5;
let mut done = false;
while !done {
x += x - 3;
if x % 5 == 0 {
done = true;
}
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
return None; //capture all other other possibility. So the while loop would surely return either a Some or a None
}
}
}
The compiler gives this error:
error[E0308]: mismatched types
--> <anon>:7:9
|
7 | while !done {
| ^ expected enum `std::option::Option`, found ()
|
= note: expected type `std::option::Option<i32>`
= note: found type `()`
I think the error source might be that a while loop evaluates to a (), thus it would return a () instead of Some(123). I don't know how to return a valid Some type inside a loop.
The value of any while true { ... } expression is always (). So the compiler expects your foo to return an Option<i32> but finds the last value in your foo body is ().
To fix this, you can add a return None outside the original while loop. You can also use the loop construct like this:
fn main() {
// run the code
foo();
fn foo() -> Option<i32> {
let mut x = 5;
loop {
x += x - 3;
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
if x % 5 == 0 {
return None;
}
}
}
}
The behaviour of while true { ... } statements is maybe a bit quirky and there have been a few requests to change it.

Combining multiple Bool return values without shortcircuiting

Swift 2 has restrictions on using bitwise operators for Bool values. This is agreeable. In ObjC it was very useful to use it when you need to execute each operand. For example:
a.isFoo() & b.isFoo() & c.isFoo()
In this case, using the bitwise & will execute each method.
If I use the logical operator &&, it will execute the first one and if it is false, the expression will return false without executing the other two operands.
I want to find the same elegant way that & works, with Bool in Swift. Is it possible?
What you were doing in Objective-C was not "elegant". It was skanky and you shouldn't have been doing it. If you want to call three methods, just call those three methods! But forming a boolean expression, you should use the logical operators, not the bitwise operators. So, for example:
let (ok1, ok2, ok3) = (a.isBool(), b.isBool(), c.isBool())
let ok = ok1 && ok2 && ok3
There is no such special operator but I would probably do it in this way:
if ![a.isBool(), b.isBool(), c.isBool()].contains(false) {
or just
let aCondition = a.isBool()
let bCondition = b.isBool()
let cCondition = c.isBool()
if aCondition && bCondition && cCondition {
but you can can also define your own operator to that.
You could mimic the same behaviour using a reduce operation on an array of your method calls, e.g.
/* example setup */
struct Foo {
let bar: Bool
init(_ bar: Bool) { self.bar = bar }
func isTrue() -> Bool { print("\(bar) foo!"); return bar }
}
let a = Foo(false)
let b = Foo(false)
let c = Foo(true)
/* naturally all three method calls will execute to construct the boolean
array, whereafter reduce will evaluate the combined conditional statement */
if [a.isTrue(), b.isTrue(), c.isTrue()].reduce(true, combine: { $0 && $1 }) {
print("Reached this ...")
} /* false foo!
false foo!
true foo! */
let d = Foo(true)
let e = Foo(true)
let f = Foo(true)
if [d.isTrue(), e.isTrue(), f.isTrue()].reduce(true, combine: { $0 && $1 }) {
print("Reached this ...")
} /* true foo!
true foo!
true foo!
Reached this ... */
or e.g. supplying your methods as variadic arguments to a function doing the method execution and combined conditional
func foo(calls: (() -> Bool)...) -> Bool {
return calls.map{ $0() }.reduce(true, combine: { $0 && $1})
}
let a = Foo(false)
let b = Foo(false)
let c = Foo(true)
if foo(a.isTrue, b.isTrue, c.isTrue) {
print("Reached this ...")
} /* false foo!
false foo!
true foo! */
let d = Foo(true)
let e = Foo(true)
let f = Foo(true)
if foo(d.isTrue, e.isTrue, f.isTrue) {
print("Reached this ...")
} /* true foo!
true foo!
true foo!
Reached this ... */
let values = [a.isFoo(), b.isFoo(), c.isFoo()]
let result = values.allSatisfy { $0 }