I am trying to write a fun which has input as a list of Strings say UserIds and return as Single<>
The method looks something like this
fun getUserSubjectsForPeriod(userIds: List<String>, startDate: String, endDate: String): Single<Pair<String, List<UserSubjects>>> {
return Observable.fromIterable(userIds)
.map {id->
userSubjectRepository.loadUserSubjects(id, startDate, endDate)
.map {
val userSubjects = userSubjectMapper.mapToDomain(it)
Pair(id,userSubjects)
}
}
}
This function is returning
Observable<Single<Pair<String, List<UserSubjects>>>> instead of Single<Pair<String, List<UserSubjects>>>.
How do I return the required output?
You can use toList() method of Observable.
I don't know the signature of userSubjectRepository.loadUserSubjects so I supposed that it should return Single
fun getUserSubjectsForPeriod(userIds: List<String>, startDate: String, endDate: String): Single<List<Pair<String, UserSubjects>>> {
return Observable.fromIterable(userIds)
.flatMapSingle { id ->
userSubjectRepository.loadUserSubjects(id, startDate, endDate)
.map {
val userSubjects = userSubjectMapper.mapToDomain(it)
Pair(id, userSubjects)
}
}.toList()
}
Related
I have a list of classes (Kotlin)
class ChallengeRecord(
val sc: String,
val participant: String,
val hr: String,
val point: Int)
how can I group by participant and sum points by them?
I want to found kotlin analogue for java
Collectors.groupingBy() or Collectors.toMap()
.groupingBy { it.participant }
.fold(0){accumulator, element -> accumulator + element.point }
.groupBy { it.participant }
.mapValues { (_, challengeRecords) -> challengeRecords.sumOf { it.point } }
I am trying to pass a function and want to use reduce on it.
This is the data class
data class TestClass(
var variable1: BigDecimal?,
var variable2: BigDecimal?
)
This is the function which I want to work
fun aggregateAll() {
helperfunc(TestClass::variable1::get,someDummmyFilledListOfTestClass)
}
fun helperfunc(function: () ->BigDecimal, testList:List<TestClass>) {
var aggregatedValue:BigDecimal = BigDecimal.ZERO
testList.map{function}.reduce{aggregatedValue,element -> aggregatedValue.add(element)}
}
fun main() {
val someDummmyFilledListOfTestClass = listOf<TestClass>(
TestClass(1.toBigDecimal()),
TestClass(199.toBigDecimal()),
TestClass(null),
TestClass(501.toBigDecimal())
)
val result = helperfunc(someDummmyFilledListOfTestClass, TestClass::variable1::get)
// trailing lambda
// or val result = helperfunc(someDummmyFilledListOfTestClass) { it.variable1 }
println(result)
}
/* lambda last parameter for trailing lambda */
fun helperfunc(testList: List<TestClass>, function: (TestClass) -> BigDecimal?): BigDecimal {
return testList
// map TestClass to BigDecimal? and filter nulls
.mapNotNull(function)
// fold with initial value, reduce will throw exception if list is empty
.fold(BigDecimal.ZERO) { acc, element -> acc.add(element) }
}
data class TestClass(
var variable1: BigDecimal?
// var variable2: BigDecimal?
)
For the map function to work, your function type needs a TestClass parameter, so it should be (TestClass) -> BigDecimal.
And your variable1 needs to be non-nullable, or it else its getter is a (TestClass) -> BigDecimal?, which doesn't match the required type.
And when you pass the function to map, don't wrap it in a lambda, because then you are just creating a function that returns your other function.
And you seem to want to use a separate aggregator rather than the first element of the list, so that means you should use fold instead of reduce.
data class TestClass(
var variable1: BigDecimal,
var variable2: BigDecimal?
)
fun aggregateAll() {
helperfunc(TestClass::variable1::get, someDummmyFilledListOfTestClass)
}
fun helperfunc(function: (TestClass) -> BigDecimal, testList:List<TestClass>) {
val sum: List<BigDecimal> =
testList.map(function).fold(BigDecimal.ZERO) { aggregatedValue, element -> aggregatedValue.add(element)}
}
I am learning Kotlin and writing code to check my understanding. I'm trying to use a toString override to print the values of a hashMap that is a property of a class. I can't get it to work. Instead I get output like "kotlin.Unit() -> kotlin.Unit". Also, I don't understand why the values of the hashMap ARE printing out before the toString output. I don't know where that output is coming from. Please help me. Thanks. Below is my code and the output I'm getting.
Code:
package ch07.ExpandoObject
import java.beans.PropertyChangeListener
import java.beans.PropertyChangeSupport
import kotlin.properties.Delegates
import kotlin.reflect.KProperty
class Person(
val name: String = "",
age: Int? = null,
var isMarried: Boolean? = null ,_attributes: kotlin.collections.HashMap<String,String>? = hashMapOf<String, String>()
)
:PropertyChangeAware()
{
var _attributes : kotlin.collections.HashMap<String,String>? = hashMapOf<String, String>()
fun setAttribute(attrName: String, value: String) {
_attributes!!.set(attrName, value)
_attributes!!.set("name", this.name)
}
override fun toString() = "Person(name=\"${name?:""}\", age=${age?:99999}, isMarried=$isMarried) " +
"${_attributes?.get("name")} " + "$name " +
this._attributes!!.forEach { (attrName, value) -> println("$attrName = $value") } +
{
for ((attrName, value) in this._attributes!!) {
println("attribute $attrName = ${this._attributes!![attrName]}")
}
}
val _age = ObservableProperty(propName = "age", propValue = age, changeSupport = changeSupport)
private val observer = {
prop: KProperty<*>, oldValue: Int, newValue: Int ->
changeSupport.firePropertyChange(prop.name, oldValue, newValue)
}
var age: Int by Delegates.observable(initialValue = age?:99999,onChange = observer)
}
class ObservableProperty(val propName: String,
var propValue: Int?, val changeSupport: PropertyChangeSupport
) {
fun getValue(): Int? = propValue
fun setValue( newValue: Int) {
val oldValue = propValue
propValue = newValue
changeSupport.firePropertyChange(propName, oldValue, newValue)
}
}
open class PropertyChangeAware {
val changeSupport = PropertyChangeSupport(this)
fun addPropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.addPropertyChangeListener(listener)
}
fun removePropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.removePropertyChangeListener(listener)
}
}
fun main(args: Array<String>) {
val p = Person("Bob", 89, isMarried = false)
val data = mapOf("lastname" to "Jones", "company" to "JetBrains")
for ((attrName, value) in data)
p.setAttribute(attrName, value)
println(p)
}
Here is the current output:
name = Bob
company = JetBrains
lastname = Jones
Person(name="Bob", age=89, isMarried=false) Bob Bob kotlin.Unit() -> kotlin.Unit
Thanks, again, for any help.
You should not use print() or println() functions inside toString() because they output their arguments to the standard output immediately instead of appending them to the string returned to the caller.
Let's examine the output kotlin.Unit() -> kotlin.Unit you're getting. It consists of two parts:
kotlin.Unit is the string representation of attributes!!.forEach { ... } expression. forEach function returns without value, and in Kotlin it's expressed by returning the Unit object value. Its string representation is appended to the string you're returning.
the second part, () -> kotlin.Unit, is also the string representation of the lambda function expression { for((attrName, value) in ...) }. This function takes no parameters, and returns without value, which means that its type is () -> Unit. Note that in Kotlin the block { ... } declares a local lambda function. If you instead want to run the code inside of that block, use the run function: run { ... }
The goal of toString function is to build the string representation of an object. And for that you can use buildString function:
override fun toString() = buildString {
append("Person(name=\"${name?:""}\", age=${age?:99999}, isMarried=$isMarried) ")
append("${_attributes?.get("name")} ").append("$name ")
this._attributes!!.forEach { (attrName, value) -> append("$attrName = $value") }
for ((attrName, value) in this._attributes!!) {
append("attribute $attrName = ${this._attributes!![attrName]}")
}
}
This function creates a StringBuilder and passes it as a receiver to its functional argument, where you call append or appendln on that receiver. Then buildString converts that string builder to a string and returns it.
Using Kotlin and spring data mongodb, I'm trying to find the most idiomatic method that can receive nullable parameters (through user defined filters) and create a MongoTemplate Query with the non-null ones (which, in Java, would be a bunch of ifs). This is what I came up with, so far, but I wonder if there's a better way:
// extending Query
open class Filter {
// case insensitive 'like' criteria
fun Query.like(field: String, value: String?) = value?.let {
this.addCriteria(Criteria.where(field).regex(it,"i"))
}
// queries from a date, to a date or between two dates
fun <T: Comparable<T>> Query.between(field: String, from: T?, to: T?) {
if (from != null || to != null) {
val criteria = where(field)
from?.let { criteria.gte(it) }
to?.let { criteria.lte(it) }
this.addCriteria(criteria)
}
}
fun Query.onlyActive(active: Boolean?) = when (active) {
true -> this.addCriteria(Criteria.where("active").`is`(true))
else -> null
}
}
// data class extending Filter()
data class myFilter(val: name: String, val type: String?, val content:String?,
val fromNum: Int?, val toNum: Int?, val fromDate: LocalDateTime?,
val toDate: LocalDateTime?, val active? = false): Filter() {
fun toQuery(): Query {
// name is a mandatory param
val query = Query.query(Criteria.where("name").`is`(name))
with(query) {
like("type", type)
like("content", content)
between("num", fromNum, toNum)
between("date", fromDate, toDate)
onlyActive(active)
}
return query
}
}
How to convert this array of String:
"2018-05-08T23:22:49Z"
"n/a"
"2018-05-07T16:37:00Z"
to an array of Date using Higher-Order Functions such as map, flatMap or reduce?
I do know that it's possible to do that using forEach, but I'm interested to involve Kotlin Higher-Order Functions:
val stringArray
= mutableListOf("2018-05-08T23:22:49Z", "n/a", "2018-05-07T16:37:00Z")
val dateArray = mutableListOf<Date>()
stringArray.forEach {
try {
val date = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
.parse(it)
dateArray.add(date)
} catch (e: ParseException) {
//* Just prevents app from crash */
}
}
Using mapNotNull
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
val dates = listOf("2018-05-08T23:22:49Z", "n/a", "2018-05-07T16:37:00Z")
.mapNotNull {
try {
format.parse(it)
} catch (e: ParseException) {
null
}
}
println(dates)
This avoids creating a list for each item in the list, it maps the bad dates to null, and mapNotNull removes the nulls from the list.
Using an extension function
You could also extract the tryOrRemove to an extension function, making the code look like this:
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
fun <T, U: Any> Iterable<T>.tryOrRemove(block:(T)->U): List<U> {
return mapNotNull {
try {
block(it)
} catch (ex: Throwable) {
null
}
}
}
val dates = listOf("2018-05-08T23:22:49Z", "n/a", "2018-05-07T16:37:00Z")
.tryOrRemove(format::parse)
println(dates)
Using filter
I have written it based on the only bad dates being n/a, which simplifies it.
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
val dates = listOf("2018-05-08T23:22:49Z", "n/a", "2018-05-07T16:37:00Z")
.filter { it != "n/a" }
.map(format::parse)
println(dates)
You're looking for a transformation that can output zero or one element per input element. This is flatMap. The result of a flatmapping function must be an Iterable, so:
val dateArray = stringArray.flatMap {
try {
listOf(SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).parse(it))
} catch (e: ParseException) {
emptyList<Date>()
}
}
Adding the following based on #pwolaq's input:
It's highly recommended to extract the SimpleDateFormat instance because it has heavyweight initialization. Further, a solution with mapNotNull is cleaner than flatMap, I wasn't aware of it. This becomes especially convenient if you add a function that I feel is missing from the Kotlin standard library:
inline fun <T> runOrNull(block: () -> T) = try {
block()
} catch (t: Throwable) {
null
}
With this in your toolbox you can say:
val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
val dateArray: List<Date> = stringArray.mapNotNull {
runOrNull { formatter.parse(it) }
}