I have a value with below type in my data class
Option<Option<List<Pair<String, Option<String>>>>>
How would I access the right-most Option<Sring>. I have tried with when expression like below
when(Option<Option<List<Pair<String, Option<String>>>>>) {
is Some -> when(Option<Option<List<Pair<String, Option<String>>>>>.t) {
is Some -> when(Option<List<Pair<String, Option<String>>>>.t) {
......
but it's not looking good. Is there any other way to cast away those Options
val t: Option<Option<List<Pair<String, Option<String>>>>> =
Some(Some(listOf(
Pair("a", Some("A")),
Pair("b", Some("B")),
Pair("c", None)
)))
val l = t.flatten()
.map { list ->
list.map { pair -> pair.second }
}
.getOrElse { emptyList() }
val first = l.getOrElse(0) { None }
val second = l.getOrElse(1) { None }
val missing = l.getOrElse(7) { None }
nested // Option<Option<List<Pair<String, Option<String>>>>>
.flatten() // Option<List<Pair<String, Option<String>>>>
.map { it.map { it.second() } } // Option<List<Option<String>>>
.sequence(Option.applicative()) // Option<Option<List<String>>>
.flatten() // Option<List<String>>
flatten gets rid of nested options, and sequence goes from List<Option<A>> to Option<List<A>>.
Related
I have a factory which includes many HTML attribute generators which returns one of them based on the type of attribute, so I wanted to see if there is a better way of doing this.
class AttributeHtmlGeneratorFactory {
fun create(property: String): AttributeHtmlGenerator {
when (property) {
"animation" -> {
return AnimationHtmlGenerator()
}
...
"left", "top" -> {
return PositionHtmlGenerator()
}
...
"scaleX" , "scaleY", ... , "direction" -> {
return UnusedAttributesHtmlGenerator()
}
this when switch has like 20 switch cases in it.
this is the interface which all these classes are using
interface AttributeHtmlGenerator {
fun generateHtml(member: KProperty1<HtmlComponentDataModel, *>, component: HtmlComponentDataModel ): String
}
and this is where and how I'm using all of these:
var result = ""
HtmlComponentDataModel::class.memberProperties.forEach { member ->
val generator = AttributeHtmlGeneratorFactory().create(member.name)
result = result.plus(generator.generateHtml(member, component))
}
return result
also, this is a simple implementation of the interface:
class ButtonFillHtmlGenerator : AttributeHtmlGenerator {
override fun generateHtml(member: KProperty1<HtmlComponentDataModel, *>, component: HtmlComponentDataModel): String {
var result = ""
member.get(component)?.let {
result = result.plus("background-color:${it};")
}
return result
}
}
is there anyway to make this better?
If you just want to reformat the when statement, I suggest you you do like this:
fun create(property: String): AttributeHtmlGenerator = when (property)
{
"animation" -> AnimationHtmlGenerator()
"left", "top" -> PositionHtmlGenerator()
"scaleX", "scaleY", "direction" -> UnusedAttributesHtmlGenerator()
else -> error("No generator found for property $property")
}
If you want to split this logic across modules, you would use a Map.
class AttributeHtmlGeneratorFactory {
private val generatorMap = mutableMapOf<String, () -> AttributeHtmlGenerator>()
init {
assignGeneratorToProperties("animation") { AnimationHtmlGenerator() }
assignGeneratorToProperties("left", "top") { PositionHtmlGenerator() }
}
fun create(property: String): AttributeHtmlGenerator {
return generatorMap[property]?.invoke() ?: error("No generator found for property $property")
}
fun assignGeneratorToProperties(vararg properties: String, provider: () -> AttributeHtmlGenerator) {
properties.forEach {
generatorMap[it] = provider
}
}
}
This way you can call assignGeneratorToProperties in parts of the code and thus split the initialization logic.
Performance-wise, when/if-else statements are really performant when you have a few cases but a HashMap outperforms them for a lot of elements. You decide what to use depending on your case.
I want to create map from list of list and I have write this code
fun getCourses(coursesCount: Int): Map<Course, Int> {
val paidCourses = mutableMapOf<Course, Int>()
for(student in data) {
for(course in student.subscribedCourses) {
if( course.isPaid ) {
paidCourses.putIfAbsent(course, 0)
paidCourses[course] = paidCourses[course]!! + 1
}
}
}
return paidCourses.toList().sortedByDescending { (_, value) -> value }.take(coursesCount).toMap()
}
I wonder how can I concise this more in Kotlin.
You can do a flatMap to flatten the "students with courses" to just a single list of all the courses, filter by isPaid, group by each course, and use eachCount to count the courses.
val paidCourses =
data.flatMap { it.subscribedCourses }
.filter { it.isPaid }
.groupingBy { it }.eachCount()
Note that this will create multiple intermediate lists and loop through them multiple times, which may be undesirable. Here's a way that avoids this, and is still quite concise:
val paidCourses = mutableMapOf<Course, Int>()
for(student in data) {
for(course in student.subscribedCourses) {
if (course.isPaid) {
paidCourses.merge(course, 1, Int::plus)
}
}
}
You can also do:
val paidCourses = mutableMapOf<Course, Int>()
for(student in data) {
student.subscribedCourses.filter { it.isPaid }
.groupingBy { it }
.eachCountTo(paidCourses)
}
You can use merge to increment the frequency.
paidCourses.merge(course, 1, Int::plus)
I have a list of recurring elements in Kotlin, say:
val result = arrayListOf<String>("AA", "BB", "CC", "AA", "BB")
I would like to group them by their value along with how many times they appear, so the output would be pairs of:
{"AA", 2}, {"BB", 2}, {"CC", 1}
I have resolved the problem using in Kotlin as follows:
val ans = result.map { it.value }
.groupBy { it }
.map { Pair(it.key, it.value.size) }
.sortedByDescending { it.second }
I want to write same code in RxKotlin for learning and tried with the following but do not know how to apply map/flatMap to achieve the result.
val source = Observable.fromIterable(result)
source.groupBy{ it }.subscribe { showresult(it) }
Try something like this:
source.groupBy { it }
.flatMapSingle { g -> g.count().map { Pair(g.getKey(), it) } }
.toSortedList { a, b -> b.second.compareTo(a.second) }
.subscribe { list -> println(list) }
I wrote this in Kotlin:
fun fromLists(cells: List<List<Double>>): Matrix {
return Matrix(cells.stream()
.map { x -> x.toDoubleArray() }
.toArray { i: Int -> Array(i, { k: Int -> DoubleArray(k) }) } )
}
Is there any way to reduce repetition in this code?
(Matrix itself is uninteresting, it just wraps an Array<DoubleArray>)
val ex1: Array<DoubleArray> = cells.map { it.toDoubleArray() }.toTypedArray()
// this should be faster, it doesn't create extra List like the previous example
val ex2: Array<DoubleArray> = Array(cells.size) { i -> cells[i].toDoubleArray() }
Kotlin 1.3.31
RxJava2
I have the following method that should get a list of users and loops through using the map operator and increase the gpa by 10. Then print the result out in the onNext. However, what I am getting printed is this:
kotlin.Unit
I was thinking that the list of users should be passed down the stream to the onNext in the subscribe method
private fun getUserListMapped() {
val disposable = getUserListFromCallable()
.map {
it.forEach { user ->
user.gpa *= 10
}
}
.subscribeOn(schedulerProvider.background())
.observeOn(schedulerProvider.ui())
.subscribe { println("users $it") }
}
This is what I am doing to get my users:
private fun getUserListFromCallable(): Observable<List<User>> {
return Observable.fromCallable { createListOfUsers() }
}
private fun createListOfUsers(): List<User> {
Thread.sleep(500L) // simulate getting from the network or local
return listOf(
User("john", "paris town", 5.6F),
User("simon", "hollands place", 2.56F),
User("lisa", "london bridge", 3.89F),
User("peter", "tokyo hills", 4.3F))
}
Many thanks for any suggestions
in your map you have to return value:
.map {
it.forEach { user ->
user.gpa *= 10
}
it
}
or you can just use doOnEach like this:
.doOnEach {
it.forEach { user ->
user.gpa *= 10
}
}
forEach method returns an Unit and that is why you see Unit printed. You should change the map operator to something like this:
.map { it.map { user -> user.apply { gpa *= 10 } } }