Efficiently matching two Flux - kotlin

I have two Flux with 2 different data types as shown below:
Flux<Dog> dogs = loadDogsFromFile()
Flux<Man> men = loadMenFromFile()
data class Dog(
val name: String,
val ownerName: String,
)
data class Man(
val name: String,
val dogOwnerName: String,
)
As you can see the one field we can use to match these two Flux objects is dogOwnerName. Right now this is how I am comparing them
val disposable = dogs.flatMap { dog ->
men.map { man->
val isEqual = comparator(dog, man)
Triple(dog, man, isEqual)
}
}.filter {x -> x.third === true }
This gets the job done but it is nowhere efficient, it keeps looping even after the desired fields are found and because of that, we have to use a filter operator to only get what we need.
Edit
Based on #marstran comment on the user input, I have large JSON files that contain dogs and men that I'm loading here:
Flux<Dog> dogs = loadDogsFromFile()
Flux<Man> men = loadMenFromFile()
After matching the dogs to their owners/men I'm building an object that I'm saving to the database like this:
val disposable = dogs.flatMap { dog ->
men.map { man->
val isEqual = comparator(dog, man)
Triple(dog, man, isEqual)
}
}.filter {x -> x.third === true }
.map{(dog,man,isEqual) ->
DogOwner(man,dog)
}.doOnNext{dogOwner -> dogOwnerRepository.save(dogOwner)}

Consider using method take(long n, boolean limitRequest) from Flux:
public final Flux<T> take(long n,
boolean limitRequest)
Take only the first N values from this Flux, if available.
using it will allow you to break iterating over man once owner would be found.
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html#take-long-boolean-
Take a look at this java example. I assume that dog and his owner have same value.
#Test
void dog_owner_test() {
int COUNT = 10000;
Flux<Integer> dogs = Flux.range(0, COUNT);
Flux<Integer> man = Flux.range(0, COUNT);
dogs.flatMap(
dog ->
man.map(m -> Tuples.of(dog, m))
.filter(tuple -> tuple.getT1().equals(tuple.getT2()))
.map(tuple -> new DogOwner(tuple.getT1(), tuple.getT2()))
.take(1, true)
)
.doOnNext(System.out::println)
// here you can save DogOwner to db
.as(StepVerifier::create)
.expectNextCount(COUNT)
.verifyComplete();
}
private static class DogOwner {
DogOwner(Integer dog, Integer owner) {
this.dog = dog;
this.owner = owner;
}
Integer dog;
Integer owner;
#Override
public String toString() {
return "DogOwner{" +
"dog=" + dog +
", owner=" + owner +
'}';
}
}

Related

Kotlin sort one List with key and Enum with key and order

I receive data from Request information as list data (List) below code. That data has a "key" parameter by which I want to sort it.
data class ApplianceSetting(
#SerializedName("key") val key: String,
#SerializedName("value") var value: Any,
(...)
I have the required order in the SettingsUtilEnum and want to sort items by that.
After that, I can convert the list using map{} the data and use the function of Enum getSettingByMode() and get the list of Enum values. Then I will sort them and convert them again to List.
But that sounds too inefficient. Is there a better way.
enum class SettingsUtilEnum(
var settingKey: String,
override val order: Int = 99,
var settingName: String = "",
) : AbstractOrderEnum {
FIRST_MODE("first.mode", 0),
SECOND_MODE("second.mode", 1),
(...)
UNKNOWN_MODE("", 99);
companion object {
#JvmStatic
fun getSettingByMode(settingKey: String): SettingsUtilEnum? {
return values().find { it.settingKey == settingKey }
}
k
private fun initDataObserver() {
(activity as FavouriteActivity).viewModel.applianceSettings.observe(activity as FavouriteActivity
) { data ->
(controlRecyclerView.adapter as FavouriteAdditionalControlsAdapter)
val adapter = (controlRecyclerView.adapter as FavouriteAdditionalControlsAdapter)
// public final var data: List<ApplianceSetting>
// old code:
// data.settings
adapter.data = sortAndGetControlModes(data)
adapter.notifyDataSetChanged()
}
}
// TODO: sortAndGetControlModes
private fun sortAndGetControlModes(data: ApplianceSettingsList) =
data.settings.map {
getSettingByMode(it.key)
?: UNKNOWN_MODE.apply {
// If in future new modes are added -> put them as tail
settingKey = it.key
}
}.sortedBy { it.order }
// error i need to return again List<ApplianceSetting>
If you want to compare keys with theirs ASCII values you can just use sortBy { it.key }
If you want to expand possibilities of comparison you can use function sortedWith with passing custom comparator as argument.
Comparator used to compare its two arguments for order. Returns zero if the arguments are equal, a negative number if the first argument is less than the second, or a positive number if the first argument is greater than the second.
Example:
You can use it like that if you want to sort by integer value of key parameter:
data.settings.sortedWith { a, b ->
when {
a.key.toInt() < b.key.toInt() -> -1
a.key.toInt() > b.key.toInt() -> 1
else -> 0
}
}
I fixed it using sortedBy and as comparator I am using received value (order) from getSettingByMode(), if item is not found (null) I give him order value of 99 and put it on tail position:
private fun sortAndGetControlModes(data: ApplianceSettingsList) =
data.settings.sortedBy {
getSettingByMode(it.key)?.order ?:99
}

Creating an object builder with error handling using Arrow - Pattern match multiple Eithers

I have class A:
class A (private var z: String, private var y: String, private var x: Int)
I want to create a failsafe builder for it. The builder should return Either the list of Exceptions (e.g. when values are missing) or the created values. What is the recommended way to create something like this? Or is there a conceptually better approach?
My own approach to it:
sealed class ABuilderException {
object MissingXValue : ABuilderException()
object MissingYValue : ABuilderException()
object MissingZValue : ABuilderException()
}
import arrow.core.Either
import arrow.core.Option
import arrow.core.none
import arrow.core.some
class ABuilder {
private var x : Option<Int> = none()
private var y : Option<String> = none()
private var z : Option<String> = none()
fun withX(x : Int) : ABuilder {
this.x = x.some();
return this;
}
fun withY(y : String) : ABuilder {
this.y = y.some();
return this;
}
fun withZ(z : String) : ABuilder {
this.z = z.some();
return this;
}
fun build() : Either<A, List<ABuilderException>> {
var xEither = x.toEither { ABuilderException.MissingXValue }
var yEither = y.toEither { ABuilderException.MissingYValue }
var zEither = z.toEither { ABuilderException.MissingZValue }
// If all values are not an exception, create A
// otherwise: Return the list of exceptions
}
}
How could I best complete the build code?
I favor a solution that avoids deep nesting (e.g. orElse or similar methods) and avoids repeating values (e.g. by recreating Tuples), because this may lead to typos and makes it harder to add/remove properties later.
First you need to change the signature of build to:
fun build() : Either<List<ABuilderException>, A>
The reason for doing that is because Either is right biased - functions like map, flatMap etc operate on the Right value and are no-op in case the value is Left.
For combining Either values you can use zip:
val e1 = 2.right()
val e2 = 3.right()
// By default it gives you a `Pair` of the two
val c1 = e1.zip(e2) // Either.Right((2, 3))
// Or you can pass a custom combine function
val c2 = e1.zip(e2) { two, three -> two + three } // Either.Right(5)
However there is an issue here, in case of an error (one of them is Left) it will fail fast and give you only the first one.
To accumulate the errors we can use Validated:
val x = none<Int>()
val y = none<String>()
val z = none<String>()
// Validated<String, Int>
val xa = Validated.fromOption(x) { "X is missing" }
// Validated<String, String>
val ya = Validated.fromOption(y) { "Y is missing" }
// Validated<String, String>
val za = Validated.fromOption(z) { "Z is missing" }
xa.toValidatedNel().zip(
ya.toValidatedNel(),
za.toValidatedNel()
) { x, y, z -> TODO() }
Validated, like Either has a zip function for combining values. The difference is that Validated will accumulate the errors. In the lambda you have access to the valid values (Int, String, String) and you can create your valid object.
toValidatedNel() here converts from Validated<String, String> to Validated<Nel<String>, String> where Nel is a list that can NOT be empty. Accumulating errors as a List is common so it's built in.
For more you can check the Error Handling tutorial in the docs.

Getting list of enums from bitmask in Kotlin

I have an enum class SettingsVisibility inside an database entity, which I am converting to a bitmask to store in the database. I am trying to reverse the conversion to bitmask, and get a list of enums as a return value. So if I have an enum with values ONE(1), TWO(2), FOUR(4), then it'll store as Enum(7). I want to take 7 and convert it to {ONE, TWO, FOUR}.
My code is below. I have the SettingsVisibility enum with integer values which are stored in the DB. When I try to retrieve from the database, Objectbox will use the given PropertyConvertor to marshall/unmarshall the data. When I want to convertToEntityProperty, it should return a list of just the saved enums, but at the moment it returns a list of all the enums. I can pass a databaseValue of 12 and it will return all enums instead of just 2 (LOCATION AND PAYMENTS).
I think the issue is the usage of enumClass.enumConstants because it gets all the values, but then the filter doesn't work on this, so I am stuck.
#Entity
data class Settings(
#Id override var id: Long = 0,
#Convert(converter = DocumentVisibilityConverter::class, dbType = Int::class)
val showItems: List<SettingsVisibility>
) : Identifiable<Long> {
lateinit var organisation: ToOne<Organisation>
constructor() : this(
showItems = emptyList(),
)
enum class SettingsVisibility(override val bit: Int) : Flags {
USERS(1),
FINANCE(2),
LOCATION(4),
PAYMENTS(8),
MESSAGES(16),
ERRORS(32),
CANCELLATIONS(64)
}
internal class DocumentVisibilityConverter
: BoxConverters.EnumFlagConverter<SettingsVisibility>(SettingsVisibility::class.java)
}
So for example, if I store the first 3, the database value will be 7 (1+2+4).
The database is ObjectBox and here are the property converters:
abstract class EnumFlagConverter<E>(private val enumClass: Class<E>) : PropertyConverter<List<E>, Int> where E : Enum<E>, E : Flags {
override fun convertToDatabaseValue(entityProperty: List<E>?): Int? {
return entityProperty?.toBitMask()?.value
}
override fun convertToEntityProperty(databaseValue: Int?): List<E>? {
return databaseValue?.let(::BitMask)?.enabledValues(enumClass)
}
}
class BitMask(val value: Int)
interface Flags {
val bit: Int
fun toBitMask() = BitMask(bit)
fun <T> BitMask.enabledValues(enumClass: Class<T>): List<T>? where T : Enum<T>, T : Flags? {
return enumClass.enumConstants?.filter(::hasFlag)
}
infix fun <T : Flags?> BitMask.hasFlag(flag: T): Boolean {
if (value == 0 || (value > 0 && flag?.bit == 0)) {
return false
}
return true
}
Maybe the logic in hasFlag is wrong, because I think that just gets every enum if it isn't 0.
Answer was to replace return true, with:
if (flag?.bit?.toByte() == null) {
return false
}
return (this.value.toByte().and(flag.bit.toByte()) == flag.bit.toByte())
This is basically: bit & mask == bit

Kotlin - Data class type String or some Object

Say I have this data class in Kotlin:
#Document(collection = Approval.COLLECTION)
data class Approval(
#Id
val id: String,
val detailId: <UNSURE HERE>
) {
companion object {
const val COLLECTION: String = "approval"
}
}
That detailID can either be a String or an object like so:
data class AIDConfiguration(
val sId: String,
val cId: String
)
However how do go about setting that type - as I can't use something like
val detailId: AIDConfiguration | String
I thought maybe make an interface, but not sure any syntax of getting that to be just a String
interface ParentConfiguration
data class AIDConfiguration(
val sId: String,
val cId: String
): ParentConfiguration
And then
val detailId: ParentConfiguration
Any help appreciated.
Thanks.
One way could be:
val detailId: Any
This is loose cause it will allow any type to be assigned to detailId.
So before usage you would have to check for the type.
fun useConfig(detailId: Any) {
if (detailId is AIDConfiguration) {
//Use detailId.sId and detailId.cId. Compiler smart casts to AIDConfiguration
} else if (detailId is String)
//Use detailId. Compiler will smart cast to String
} else {
//throw some exception here.
}
}
You might want to use some validations when setting the configuration as well. Check whether the type is AIDConfiguration or String.
A little more tighter would be to have a parent configuration class. Such as ParentConfiguration and have AIDConfiguration and StringConfiguration as subclasses.
So then it becomes:
interface ParentConfiguration
data class AIDConfiguration(
val sId: String,
val cId: String
): ParentConfiguration
data class StringConfiguration(
val conf: String
): ParentConfiguration
data class Approval(
val id: String,
val detailId: ParentConfiguration
)
val stringConfigApproval = Approval(id = "Test1", detailId = StringConfiguration("String Conf"))
val aidConfApproval = Approval(id = "Test2", detailId = AIDConfiguration(sId = "SID", cId = "CID"))
Would recommend checking out the kotlin docs.
https://kotlinlang.org/docs/tutorials/kotlin-for-py/inheritance.html
https://kotlinlang.org/docs/reference/typecasts.html
Kotlin being a statically typed language, you can't just specify multiple types. An interface is a good option to do so, but since String is not implemented by you, you cannot change its signature.
One of the work around should be to use Either like this:
sealed class Either<out L, out R> {
data class Left<out L>(val a: L) : Either<L, Nothing>()
data class Right<out R>(val b: R) : Either<Nothing, R>()
/**
* Returns true if this is a Right, false otherwise.
* #see Right
*/
val isRight get() = this is Right<R>
/**
* Returns true if this is a Left, false otherwise.
* #see Left
*/
val isLeft get() = this is Left<L>
/**
* Applies fnL if this is a Left or fnR if this is a Right.
* #see Left
* #see Right
*/
fun fold(fnL: (L) -> Any, fnR: (R) -> Any): Any =
when (this) {
is Left -> fnL(a)
is Right -> fnR(b)
}
}
And then specify your variable like this:
val detailId: Either<AIDConfiguration, String>
And when you want to do some specific operation, just call the fold method like:
detailId.fold({ /* use $it as AIDConfiguration */ }, { /* use $it as String */ })
EDIT: You could also make aliases for your purpose in your projects for better readability check https://kotlinlang.org/docs/reference/type-aliases.html

can you join two tables and result with an obj (from first table) containing a list of obj(from the second table)

First of all my code:
Table 1:
object Company : Table() {
val name = varchar("pk_name", 250)
override val primaryKey = PrimaryKey(name, name = "pk_company_constraint")
}
Table 2&3:
object Sector : IntIdTable() {
val name = varchar("fk_name", 50).references(MainSector.name)
val alias = varchar("alias", 50).nullable()
val companyName = varchar("fk_company_name", 250).references(Company.name, onDelete = ReferenceOption.CASCADE)
}
object MainSector : Table() {
val name = varchar("pk_name", 50)
override val primaryKey = PrimaryKey(name, name = "pk_main_sector_constraint")
}
My Problem:
I need to parse the result into a DTO that looks like this:
data class CompanyDTO (
val companyName: String,
val sectorList: List<SectorDTO>
)
data class SectorDTO (
val mainSectorName: String,
val sectorAlias: String
)
I am able to get a Company with the first Sector from the database, but i have no idea how to get a list of them.
My try:
override fun retrieveCompanies(vararg names: String): List<CompanyDTO> {
var retlist: List<CompanyDTO> = emptyList()
if (names.isEmpty()){
retlist = transaction {
(Company innerJoin Sector)
.select{Company.name eq Sector.companyName}
.map { CompanyDTO(it[Company.name], listOf(
SectorDTO(it[Sector.name], it[Sector.alias]?: "")
)) }
}
} else {
//return specific
}
return retlist
}
If no arguments are given i want to return all companies from the database, if arguments are given i want to return only companies with given name.
I canĀ“t find anything about this topic in the official documentation, please send help
If Company could not have any Sector you need to use leftJoin and then your code could be like:
Company.leftJoin.Sector.selectAll().map {
val companyName = it[Company.name]
val sector = it.tryGet(Sector.name)?.let { name ->
SectorDTO(name, it[Sector.alias].orEmpty())
}
companyName to sector
}.groupBy({ it.first }, { it.second }).map { (companyName, sectors) ->
CompanyDTO(companyName, sectors.filterNotNull())
}