How to simplify when expression in kotlin - 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)
}

Related

Parsing Arithmetic String Operations in Kotlin

If I have a string that has an arithmetic expression with paranthesis like:
((4 * 7) / 2) - 7
How do I evaluate it automatically? In particular with Kotlin. I heard that you need to make a parser, so how can I do so in Kotlin and have all the necessary basic operations in the example as such?
fun evaluate(str: String): Double {
data class Data(val rest: List<Char>, val value: Double)
return object : Any() {
fun parse(chars: List<Char>): Double {
return getExpression(chars.filter { it != ' ' })
.also { if (it.rest.isNotEmpty()) throw RuntimeException("Unexpected character: ${it.rest.first()}") }
.value
}
private fun getExpression(chars: List<Char>): Data {
var (rest, carry) = getTerm(chars)
while (true) {
when {
rest.firstOrNull() == '+' -> rest = getTerm(rest.drop(1)).also { carry += it.value }.rest
rest.firstOrNull() == '-' -> rest = getTerm(rest.drop(1)).also { carry -= it.value }.rest
else -> return Data(rest, carry)
}
}
}
fun getTerm(chars: List<Char>): Data {
var (rest, carry) = getFactor(chars)
while (true) {
when {
rest.firstOrNull() == '*' -> rest = getTerm(rest.drop(1)).also { carry *= it.value }.rest
rest.firstOrNull() == '/' -> rest = getTerm(rest.drop(1)).also { carry = it.value / carry }.rest
else -> return Data(rest, carry)
}
}
}
fun getFactor(chars: List<Char>): Data {
return when (val char = chars.firstOrNull()) {
'+' -> getFactor(chars.drop(1)).let { Data(it.rest, +it.value) }
'-' -> getFactor(chars.drop(1)).let { Data(it.rest, -it.value) }
'(' -> getParenthesizedExpression(chars.drop(1))
in '0'..'9', ',' -> getNumber(chars)
else -> throw RuntimeException("Unexpected character: $char")
}
}
fun getParenthesizedExpression(chars: List<Char>): Data {
return getExpression(chars)
.also { if (it.rest.firstOrNull() != ')') throw RuntimeException("Missing closing parenthesis") }
.let { Data(it.rest.drop(1), it.value) }
}
fun getNumber(chars: List<Char>): Data {
val s = chars.takeWhile { it.isDigit() || it == '.' }.joinToString("")
return Data(chars.drop(s.length), s.toDouble())
}
}.parse(str.toList())
}
val expresssion = "((4 * 7) / 2) - 7"
val result = evaluate(expresssion)
println(result) // Output: 7.0
Look up "Math parser" on google. These are various dependencies you can add that allow you to then parse string input into mathematical formulas.

Kotlin idiomatic way to check a condtion and do something if fail

converting java to kotlin,
java code
private boolean hasEndpoint() {
if (mSettings == null || mSettings.getEndpoint() == null) {
if (isDebugMode()) {
throw new IllegalArgumentException("endpoint is not set !!!");
}
return false;
}
return true;
}
public void doAction_1(...) {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET");
return;
}
//do the action with valid endpoint
doSomething_1(mSettings.getEndpoint());
}
the kotlin:
private fun hasEndpoint(): Boolean {
if (mSettings?.endpoint == null) {
require(!isDebugMode) { "endpoint is not set !!!" }
return false
}
return true
}
fun doAction_1() {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
return
}
//do the action with valid endpoint
doSomething_1(mSettings!!.getEndpoint());
}
There are multiple functions (i.e. doAction_1(), doAction_2() ...) doing the same check using hasEndpoint().
What is Kotlin idiomatic way to do something like this?
You can use a concept similar to Python decorators:
// Add your check here
fun withCheck(action: () -> Unit) {
if (!hasEndpoint()) {
callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
return
}
action()
}
// Add your actions enclosed with `withCheck`
fun action1() = withCheck {
doSomething_1(mSettings!!.getEndpoint());
}
fun action2() = withCheck {
doSomething_2(mSettings!!.getEndpoint());
}
You can use a property instead of a function for hasEndpoint or rather hasNoEndpoint and use when in place of if else
private val hasNoEndpoint: Boolean
get() = when {
mSettings?.endpoint != null -> false
isDebugMode -> throw IllegalArgumentException("endpoint is not set !!!")
else -> true
}
// use this in withCheck function as in enzo's answer
fun withEndpoint(action: () -> Unit): Unit = when {
hasNoEndpoint -> callback.onError(ENDPOINT_UNINITIALIZED, "ERROR_END_POINT_NOT_SET")
else -> action()
}

MVVM Respository

i have a error on code that says
overload resolution ambiguity. all these functions match
class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() = movieDao.streamAll()
.onErrorResumeNext{
apiService.getMyMovie()
.doOnSuccess {
if (it.results.isEmpty()){
}else{
movieDao.deleteAll()
it.results.let {
Timber.d("input data")
val semuadata = it.map { data -> Movie.from(data) }
movieDao.insert(semuadata)
}
}
}
}
}
also there something like this in my error
enter image description here
You simply have to specify the parameter you take in onErrorResumeNext:
i have a error on code that says
overload resolution ambiguity. all these functions match
class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() = movieDao.streamAll()
.onErrorResumeNext{ next: Publisher<List<Movie>> ->
apiService.getMyMovie()
.doOnSuccess {
if (it.results.isEmpty()){
}else{
movieDao.deleteAll()
it.results.let {
Timber.d("input data")
val semuadata = it.map { data -> Movie.from(data) }
movieDao.insert(semuadata)
}
}
}
}
}

Idiomatic Arrow

I have the following method:
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain).map { it.toEntity() }.fold({ Either.left(it) }) { company ->
with (companyRepository) {
isCompanyExists(company).fold({ Either.left(it) }) { isExists ->
if (isExists) return#with Either.left(CompanyNameExists(companyDomain))
createCompany(company).fold({ Either.right(companyDomain) }) { Either.left(it) }
}
}
}
Is there a better/more idiomatic way to write this using Arrow?
It is hard to refactor because I can only assume what used methods should return. But I guess the methods returns MaybeError. In this case we can omit fold({ Either.left(it) }) and we can use map or flatMap.
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain)
.map { it.toEntity() }
.flatMap { company ->
companyRepository.isCompanyExists(company)
.flatMap { isExists ->
if (isExists) {
MaybeError.left(CompanyNameExists(companyDomain))
} else {
companyRepository.createCompany(company)
}
}
}

Single with flowable?

Try in rxJava2 Kotlin combine Single with Flowable but nothing not happening:
Does not undrstand what wrong
Flowable.create<Int>({ emmit ->
loadNewListener = object :Listener {
override fun onEmit(id: Int) {
emmit.onNext(id)
}
}
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.flatMapSingle {
loadNew(id = it.id)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ (data:Data) ->
}, {
Timber.e("Failed load data ${it.message}")
})
my method is returning Single:
private fun loadNew(id: Int): Single<Data> {
return when (pdfType) {
CASE_0 -> {
Single.create<Data> { emmit ->
service.get("data")
.enqueue(
object : Callback<Void> {
override fun onFailure(call: Call<Void>?, t: Throwable?) {
// failure
}
override fun onResponse(call: Call<Void>?, response: Response<Void>?) {
emmit.onSuccess(it.data)
}
}
}//single
}//case_0
CASE_1 -> 1Repository.loadsome1Rx(id = id).map { it.getData() }
CASE_2 -> 2Repository.loadsom2LocalRx(id = id).map { it.getData() }
else -> {
throw java.lang.RuntimeException("$this is not available type!")
}
}
What is wrong im my code?
Need Maby call Single in Flowable subscribe() seppurate
like this?
Flowable.create<Int>({ emmit ->
loadNewListener = object :Listener {
override fun onEmit(id: Int) {
emmit.onNext(id)
}
}
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe({
loadNew(id = it.id)
}, {
Timber.e("")
})
This code is workin but looks not simple as via combine try.
This simple example based on your code is working
var i = 0
fun foo() {
Flowable.create<Int>({ emmit ->
emmit.onNext(i)
i++
}, BackpressureStrategy.LATEST)
.debounce(500, TimeUnit.MILLISECONDS)
.flatMapSingle {
Single.create<String> { emmit ->
emmit.onSuccess("onSuccess: $it")
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Log.i("RX", "Subscribe: $it")
}, {
it.printStackTrace()
})
}
Check SingleEmitter.onSuccess() and SingleEmitter.onError() is called in all cases in when (pdfType)...
As #Stas Bondar said in answer below This simple example based on your code is working!!
Problem was in loadNewListener .
It does not init in time and has null value when need. Call create Flowable on init ViewModel but loadNewListener did not have time to create when i call him from fragment.
loadNewListener = object :Listener{...}
Becuse need some time mutch for init rxJava expression!
And combine flowable with single via flatMapSingle spent more time than just call single on flowable dubscrinbe!
So use temp field:
private var temp: Temp? = null
fun load(id: Int) {
loadNewListener.apply {
when {
this != null -> load(id = id)
else -> userEmitPdfTemp = Temp(id = id)
}
}
}
Flowable.create<Data>({ emmit ->
userEmitPdfTemp?.let {id->
emmit.onNext(Data(id))
userEmitPdfTemp =null
}
loadNewListener = object :Listener {
override fun load(id: Int) {
emmit.onNext(Data(id))
}
}
}