Transform a list, filtering out the items that cause an exception - kotlin

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) }
}

Related

How to hand over Boolean operator as parameter in Kotlin?

I have a function which has quite a lot lines. In that function I have a .filter{} like:
fun getMyListForFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag == Query.IS_FOO }
.map{
//..mappings
}
}
and then I have a second function just to retrieve queries which are NOT Foo:
fun getMyListForNotFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag != Query.IS_FOO }
.map{
//..mappings
}
}
As you can the only difference is the == or != operator in the .filter function. Although I have all the previous lines duplicated..
I bet there is a nice Kotlin way to enhance this code?
Pass a predicate as a parameter to your function for filtering the list.
fun getMyList(predicate: (YourType) -> Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter(predicate)
.map{
//..mappings
}
}
Usage:
val listForFoo = getMyList { it.flag == Query.IS_FOO }
val listForNotFoo = getMyList { it.flag != Query.IS_FOO }
OR, if you just want to pass a Boolean, you can also do that:
fun getMyList(filterFoo: Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter {
val isFoo = it.flag == Query.IS_FOO
if(filterFoo) isFoo else !isFoo
}
.map{
//..mappings
}
}
I would use partition directly.
I created a sample in kotlinlang.org's playground and it looks like this:
// Given a "thing"
data class Thing(val id: Int, val isFoo: Boolean)
// Have a function that simplifies this:
fun filterThings(source: List<Thing>) = source.partition { it.isFoo }
// Alternatively, you could have a more generic one:
fun filterThings(source: List<Thing>,
predicate: ((Thing) -> Boolean)) = source.partition(predicate)
// And you can use either like so:
// Given the source
val source = listOf(Thing(1, true),
Thing(2, true),
Thing(3, false),
Thing(4, true),
Thing(5, false),
Thing(6, false))
// Filter them with the non-configurable version:
val results = filterThings(source)
// or the more configurable one where *you* supply the predicate:
val results = filterThings(source) { it.isFoo }
The results are going to be:
results.first is going to be the one that pass the predicate, and the rest will be in results.second:
results.first = [Thing(id=1, isFoo=true), Thing(id=2, isFoo=true), Thing(id=4, isFoo=true)]
results.second = [Thing(id=3, isFoo=false), Thing(id=5, isFoo=false), Thing(id=6, isFoo=false)]

incrementing hash map count in Kotlin

I have the function below. However, when I pass a string to it, I get the following error:
error: operator call corresponds to a dot-qualified call 'charCountMap.get(c).plus(1)' which is not allowed on a nullable receiver 'charCountMap.get(c)'. charCountMap.put(c, charCountMap.get(c) + 1)
private fun characterCount(inputString:String) {
val charCountMap = HashMap<Char, Int>()
val strArray = inputString.toCharArray()
for (c in strArray)
{
if (charCountMap.containsKey(c))
{
charCountMap.put(c, charCountMap.get(c) + 1)
}
else
{
charCountMap.put(c, 1)
}
}
}
The Kotlin Standard Library has groupingBy and eachCount for this purpose, you don't need to do any of this manually:
private fun characterCount(inputString:String) {
val charCountMap : Map<Char, Int> = inputString.groupingBy { it }.eachCount()
}
Note that I put the type on charCountMap for clarity, but it can be left off and inferred.
There is nice compute method in HashMap for this:
private fun characterCount(inputString:String) = hashMapOf<Char, Int>().also { charCountMap ->
inputString.forEach { charCountMap.compute(it) { _, v -> if (v == null) 1 else v + 1 } }
}
Both the other answers are correct. Todd's answer is right, you don't need to write a function for this. Just use the standard library. And if you are going to write a function that updates maps, Михаил Нафталь's suggestion to use compute() to handle updating existing values is also good.
However, if you're just doing this an an exercise, here are three suggestions to fix/improve your algorithm:
Instead of get(), use getValue(), which does not return null. It will raise an exception if the element does not exist, but you already checked for that.
Use the [] operator instead of put() (no need to, it's just nicer syntax).
You don't need to call toCharArray() because Strings are already iterable.
if (charCountMap.containsKey(c))
{
charCountMap[c] = charCountMap.getValue(c) + 1
}
else
{
charCountMap[c] = 1
}
Rewriting the whole thing using standard formatting:
fun characterCount(inputString: String): Map<Char, Int> {
val charCountMap = mutableMapOf<Char, Int>()
for (c in inputString) {
if (charCountMap.containsKey(c)) {
charCountMap[c] = charCountMap.getValue(c) + 1
} else {
charCountMap[c] = 1
}
}
return charCountMap
}

Kotlin, how can I make my code shorter in this spaghetti code?

I'm working on readlines now and can I make this few if's shorter? I'm making a validation to what user is sending to me. The filed cant be empty or null. I have 3 important things that user has to write in field and every three times I have to check the same... .
fun readlinesToAddEntryAndValidation(): List<String> {
println(ENTER_DESCRIPTION_ID_TEKST)
val entryId: String? = readLine()
if (!entryId.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_NAME_TEKST)
val name: String? = readLine()
if (!name.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_TEKST_TEKST)
val tekst: String? = readLine()
if (!tekst.isNullOrEmpty()) {
return listOf(entryId, name, tekst)
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
}
Try to avoid cognitive complexity one of the things is avoid nesting. Also when an if always returns something. An else statement is not needed
fun readlinesToAddEntryAndValidation(): List<String> {
println(ENTER_DESCRIPTION_ID_TEKST)
val entryId: String? = readLine()
if (entryId.isNullOrEmpty()) {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
println(ENTER_DESCRIPTION_NAME_TEKST)
val name: String? = readLine()
if (!name.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_TEKST_TEKST)
val tekst: String? = readLine()
if (!tekst.isNullOrEmpty()) {
return listOf(entryId, name, tekst)
}
}
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
You could do something like this:
fun readlinesToAddEntryAndValidation() : List<String> {
fun read(message: String): String? {
println(message)
val line = readLine()
return if (line.isNullOrEmpty()) null else line
}
read(ENTER_DESCRIPTION_ID_TEKST)?.let { entryId ->
read(ENTER_DESCRIPTION_NAME_TEKST)?.let { name ->
read(ENTER_DESCRIPTION_TEKST_TEKST)?.let { tekst ->
return listOf(entryId, name, tekst)
}
}
}
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
I wouldn't normally recommend nesting too much, but I feel like that's fairly readable with only three parameters, and the null checking means it short-circuits as soon as you run into a problem.
Making user to reenter all previous (independent!) values after his mistake in the middle of the input is a bad UI.
If user failed to correctly input some entry, you need to ask him to reenter only this single item (until he eventually do it right):
fun read(inputMessage: String, errorMessage: String = EMPTY_READLINE_ERROR): String {
println(inputMessage)
var line: String? = readLine()
while (line.isNullOrEmpty()) {
println(errorMessage)
println(inputMessage)
line = readLine()
}
return line
}
With this auxilary function, whole program become a single-liner:
fun readlinesToAddEntryAndValidation() =
listOf(
ENTER_DESCRIPTION_ID_TEKST,
ENTER_DESCRIPTION_NAME_TEKST,
ENTER_DESCRIPTION_TEKST_TEKST
).map { read(it) }

Kotlin way in conditional flows

What would be the best kotlin way to have the following logic?
if (it.records.isNotEmpty()) {
if (it.records[0].fields.isNotEmpty()) {
if (it.records[0].fields["lastModifiedDate"] != null) {
RECORD_DATA_LAST_MODIFIED_DATE_FORMAT.parse(
it.records[0].fields["lastModifiedDate"])
} else {
Date(0)
}
} else {
Date(0)
}
} else {
Date(0)
}
Since you didn't provide all the code necessary to run your code I decided to create 2 classes and a function
data class Musician(
val records: List<Record>
)
data class Record(
val fields: Map<String, String>
)
fun test(mus: Musician): Date { }
Only by using the elvis operator and some common syntax you could get something like this:
fun test(mus: Musician): Date {
val sdf = SimpleDateFormat("dd/MM/yyyy")
return if(mus.records.isNotEmpty() &&
mus.records[0].fields.isNotEmpty())
sdf.parse(mus.records[0].fields["lastModifiedDate"]) ?: Date(0)
else
Date(0)
}
And if you want to use even more Kotlin fun stuff you could create somethig like:
fun test(mus: Musician): Date {
val sdf = SimpleDateFormat("dd/MM/yyyy")
return mus.records.firstOrNull()?.fields?.get("lasModifiedDate")?.let {
sdf.parse(it)
} ?: Date(0)
}
This is probably not the best way to handle this situation, but these are some options of what you can do with Kotlin
Your question is missing some detail, but probably something like this:
val date = it.records[0]?.fields["lastModifiedDate"]?.let { RECORD_DATA_LAST_MODIFIED_DATE.parse(it)} ?: Date(0)

RxJava Filter on Error

This question is loosely related to this question, but there were no answers. The answer from Bob Dalgleish is close, but doesn't support the potential error coming from a Single (which I think that OP actually wanted as well).
I'm basically looking for a way to "filter on error" - but don't think this exists when the lookup is RX based. I am trying to take a list of values, run them through a lookup, and skip any result that returns a lookup failure (throwable). I'm having trouble figuring out how to accomplish this in a reactive fashion.
I've tried various forms of error handling operators combined with mapping. Filter only works for raw values - or at least I couldn't figure out how to use it to support what I'd like to do.
In my use case, I iterate a list of IDs, requesting data for each from a remote service. If the service returns 404, then the item doesn't exist anymore. I should remove non-existing items from the local database and continue processing IDs. The stream should return the list of looked up values.
Here is a loose example. How do I write getStream() so that canFilterOnError passes?
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import org.junit.Test
class SkipExceptionTest {
private val data: Map<Int, String> = mapOf(
Pair(1, "one"),
Pair(2, "two"),
Pair(4, "four"),
Pair(5, "five")
)
#Test
fun canFilterOnError() {
getStream(listOf(1, 2, 3, 4, 5))
.subscribeOn(Schedulers.trampoline())
.observeOn(Schedulers.trampoline())
.test()
.assertComplete()
.assertNoErrors()
.assertValueCount(1)
.assertValue {
it == listOf(
"one", "two", "four", "five"
)
}
}
fun getStream(list: List<Int>): Single<List<String>> {
// for each item in the list
// get it's value via getValue()
// if a call to getValue() results in a NotFoundException, skip that value and continue
// mutate the results using mutate()
TODO("not implemented")
}
fun getValue(id: Int): Single<String> {
return Single.fromCallable {
val value: String? = data[id]
if (value != null) {
data[id]
} else {
throw NotFoundException("dat with id $id does not exist")
}
}
}
class NotFoundException(message: String) : Exception(message)
}
First .materialize(), then .filter() on non-error events, then .dematerialize():
getStream(/* ... */)
.materialize()
.filter(notification -> { return !notification.isOnError(); })
.dematerialize()
I ended up mapping getValue() to Optional<String>, then calling onErrorResumeNext() on that and either returning Single.error() or Single.just(Optional.empty()). From there, the main stream could filter out the empty Optional.
private fun getStream(list: List<Int>): Single<List<String>> {
return Observable.fromIterable(list)
.flatMapSingle {
getValue(it)
.map {
Optional.of(it)
}
.onErrorResumeNext {
when (it) {
is NotFoundException -> Single.just(Optional.empty())
else -> Single.error(it)
}
}
}
.filter { it.isPresent }
.map { it.get() }
.toList()
}