How to typesafe reduce a Collection of Either to only Right - kotlin

Maybe a stupid question but I just don't get it.
I have a Set<Either<Failure, Success>> and want to output a Set<Success> with Arrow-kt.

You can map the set like this for right:
val successes = originalSet.mapNotNull { it.orNull() }.toSet()
or if you want the lefts:
val failures = originalSet.mapNotNull { it.swap().orNull() }.toSet()
The final toSet() is optional if you want to keep it as a Set as mapNotNull is an extension function on Iterable and always returns a List
PS: No stupid questions :)
Update:
It can be done avoiding nullables:
val successes = originalSet
.map { it.toOption() }
.filter { it is Some }
.toSet()
We could potentially add Iterable<Option<A>>.filterSome and Iterable<Either<A, B>.mapAsOptions functions.
Update 2:
That last example returns a Set<Option<Success>>. If you want to unwrap the results without using null then one thing you can try is to fold the Set:
val successes = originalSet
.fold(emptySet<Success>()) { acc, item ->
item.fold({ acc }, { acc + it })
}
This last option (unintended pun) doesn't require the use of Option.

Related

How to filter elements in one list by a property value not present in elements in another list?

I have the following code snippet
val cachedNews = listOf(News(9, "https://009"), News(8, "https://234"), News(7, "https://345"))
val freshNews = listOf(News(1, "https://123"), News(2, "https://234"), News(3, "https://345"))
val result = freshNews.filter {fresh -> filter(cachedNews, fresh)}
private fun filter(cached: List<News>, fresh: News): Boolean {
cached.forEach { cachedItem ->
if (cachedItem.url == fresh.url) return true
}
return false }
When the code runs if cachedItem.url == fresh.url the list is filtered and the result is a list where the urls of the two lists are identical. However when i reverse equality like so cachedItem.url != fresh.url the list is not filtered at all. The sequence of execution changes.
When using the == sign, the first item of freshNews is compared with the first Item of cachedNews after that the secondItem of freshNews is compared with secondItem of cachedNews and so on.
When I use the != sign the all items of freshNews are compared against only the firstItem of cachedNews ??
Am I missing something or is my code just wrong?
I'm not sure what the specific problem is because your approach is quite confusing. Your custom filter function is actually more like a contains function.
What might be useful is to:
Extract the cached URLs to a set
Filter the new results by URLs that are not in the set.
fun main() {
val cachedNews = listOf(News(9, "https://009"), News(8, "https://234"), News(7, "https://345"))
val freshNews = listOf(News(1, "https://123"), News(2, "https://234"), News(3, "https://345"))
val cachedUrls = cachedNews.map { it.url }.toSet()
val result = freshNews.filterNot { cachedUrls.contains(it.url) }
println(result)
}
Result:
[News(id=1, url=https://123)]

About binarySearch() of Kotlin List

I ran the examples in the official Kotlin documentation in the local Android Studio, and found that the results are different from what I expected, but I don’t know what is causing this?
data class Produce(
val name: String,
val price: Double
)
This is the data class I defined
val list2 = listOf(
Produce("AppCode", 52.0),
Produce("IDEA", 182.0),
Produce("VSCode", 2.75),
Produce("Eclipse", 1.75)
)
this is my source list
println(list2.sortedWith(compareBy<Produce> {
it.price
}.thenBy {
it.name
}))
The output on the console is:
[Produce(name=Eclipse, price=1.75), Produce(name=VSCode, price=2.75), Produce(name=AppCode, price=52.0), Produce(name=IDEA, price=182.0)]
I call binarySearch() like this
println("result: ${
list2.binarySearch(
Produce("AppCode", 52.0), compareBy<Produce> {
it.price
}.thenBy {
it.name
}
)
}")
I think the result should be 2, but it is 0
result: 0
I don't know why it turned out like this. Plase help me . thanks a lot
sortedWith() does not modify the list, it returns a new, sorted collection. When calling list2.binarySearch() you still search through original, unsorted list.
You need to either do something like:
list2.sortedWith().binarySearch()
Or create your list with mutableListOf() and then use sort() which sorts in-place.
Broot is right. You need to pass the sorted list to the binarySearch() function. To clarify in code:
val comparator = compareBy<Produce> { it.price }.thenBy { it.name }
val sorted = list2.sortedWith(comparator)
println(sorted.joinToString("\n"))
val foundIndex = sorted.binarySearch(Produce("AppCode", 52.0), comparator)
println("Found at: $foundIndex")
Result:
Produce(name=Eclipse, price=1.75)
Produce(name=VSCode, price=2.75)
Produce(name=AppCode, price=52.0)
Produce(name=IDEA, price=182.0)
Found at: 2

How to combine a lot of LiveDatas together and transform into a single entity?

I am looking to find a way to combine some LiveData functions from my Dao and transform them into a single entity. I want to create something like this:
private val combinedValues(ld1, ld2, ld3, ld4){
first, second, third, fourth -> CombinedLiveDataValues(first, second, third, fourth)
}
val combinedEntity: LiveData<Any> = Transformations.map(combinedValues){ it->
val something = it.first.map (etc...)
}
How to create a way of not duplicating code and generalising and adding dozens of liveDatas together?
You can use something like this
fun<T> combine(context: AppCompatActivity, vararg input: LiveData<T>): LiveData<T> {
val output = MutableLiveData<T>()
input.forEach {
it.observe(context, androidx.lifecycle.Observer { value ->
output.value = value
})
}
return output
}

Kotlin nested for loops to asSequence

I'm trying to convert my nested for loop to asSequence in Kotlin. Here, my goal is to get and update the value of all my object array from another object array with the same key.
nested for loop:
val myFields = getMyFields()
val otherFields = getOtherFields()
for (myField in myFields) { // loop tru the my fields
for (otherField in otherFields) { // find the same fields
if (myField.key == otherField.key) { // if the same, update the value
val updatedMyField = myField.copy(value = otherValue.value)
myFields[myFields.indexOf(myField)] = updatedMyField // update my field value
break
}
}
}
What I've tried:
val updatedMyFields = getMyFields().asSequence()
.map { myField ->
getOtherFields().asSequence()
.map { otherField ->
if (myField.key == otherField.key) {
return#map otherField.value
} else {
return#map ""
}
}
.filter { it?.isNotEmpty() == true }
.first()?.map { myField.copy(value = it.toString()) }
}
.toList()
but this does not compile as it will return List<List<MyField>>.
I'm just looking for something much cleaner for this.
As comments suggest, this would probably be much more efficient with a Map.
(More precisely, a map solution would take time proportional to the sum of the list lengths, while the nested for loop takes time proportional to their product — which gets bigger much faster.)
Here's one way of doing that:
val otherFields = getOtherFields().associate{ it.key to it.value }
val myFields = getMyFields().map {
val otherValue = otherFields[it.key]
if (otherValue != null) it.copy(value = otherValue) else it
}
The first line creates a Map from the ‘other fields’ keys to their values.  The rest then uses it to create a new list from ‘my fields’, substituting the values from the ‘other fields’ where present.
I've had to make assumptions about the types &c, since the code in the question is incomplete, but this should do the same.  Obviously, you can change how it merges the values by amending the it.copy().
There are likely to be even simpler and more efficient ways, depending on the surrounding code.  If you expanded it into a Minimal, Complete, and Verifiable Example — in particular, one that illustrates how you already use a Map, as per your comment — we might be able to suggest something better.
Why do you want to use asSequence() ? You can go for something like that:
val myFields = getMyFields()
val otherFields = getOtherFields()
myFields.forEach{firstField ->
otherFields.forEach{secondField ->
if (firstField.key == secondField.key) {
myFields[myFields.indexOf(firstField)] = secondField.value
}
}
}
This will do the same job than your nested for loop and it's easier to read, to understand and so to maintain than your nested asSequence().

Is it possible to parameterize queries or parameters for an Acolyte ScalaCompositeHandler?

Background:
I have attempted to accomplish the question defined here, and I have not been able to succeed. Acolyte requires you to define the queries and parameters you want to handle within a match expression, and the values used in match expressions must be known at compile time. (Note, however, that this StackOverflow answer appears to provide a way around this limitation).
If this is indeed not possible, the inability to dynamically define the parameters and queries for Acolyte would be, for my use case, a severe limitation of the framework. I suspect this would be a limitation for others as well.
One SO user who has advocated for the use of Acolyte across a handful of questions stated in this comment that it is possible to dynamically define queries and their responses. So, I have opened this question as an invitation for someone to show that to be the case.
Question:
Using Acolyte, I want to be able to encapsulate the logic for matching queries and generating their responses. This is a desired feature because I want to keep my code DRY. In other words, I am looking for something like the following pseudo-code:
def generateHandler(query: String, accountId: Int, parameters: Seq[String]): ScalaCompositeHandler = AcolyteDSL.handleQuery {
parameters.foreach(p =>
// Tell the handler to handle this specific parameter
case acolyte.jdbc.QueryExecution(query, ExecutedParameter(accountId) :: ExecutedParameter(p) :: Nil) =>
someResultFunction(p)
)
}
Is this possible in Acolyte? If so, please provide an example.
It is indeed possible to parameterize queries and/or parameters by utilizing pattern matching.
See the code below for an example:
import java.sql.DriverManager
import acolyte.jdbc._
import acolyte.jdbc.Implicits._
import org.scalatest.FunSpec
class AcolyteTest extends FunSpec {
describe("Using pattern matching to extract a query parameter") {
it("should extract the parameter and make it usable for dynamic result returning") {
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val handlerName = "testOneHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
rows.append(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "hello world")
val resultSet = preparedStatement.executeQuery()
resultSet.next()
assertResult(resultSet.getString(1))("hello world")
}
it("should support a slightly more complex example") {
val firstResult = "The first result"
val secondResult = "The second result"
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val results: Map[String, RowList1.Impl[String]] = Map(
"one" -> rows.append(firstResult),
"two" -> rows.append(secondResult)
)
def getResult(parameter: String): QueryResult = {
results.get(parameter) match {
case Some(row) => row.asResult()
case _ => acolyte.jdbc.QueryResult.Nil
}
}
val handlerName = "testTwoHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
getResult(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "one")
val resultSetOne = preparedStatement.executeQuery()
resultSetOne.next()
assertResult(resultSetOne.getString(1))(firstResult)
preparedStatement.setString(1, "two")
val resultSetTwo = preparedStatement.executeQuery()
resultSetTwo.next()
assertResult(resultSetTwo.getString(1))(secondResult)
}
}
}