Преобразовать список, отфильтровывая элементы, вызывающие исключение - PullRequest
0 голосов
/ 09 мая 2018

Как преобразовать этот массив строк:

"2018-05-08T23:22:49Z" "n/a" "2018-05-07T16:37:00Z"

в массив дат с использованием функций высшего порядка, таких как map, flatMap или reduce?

Я знаю, что это можно сделать с помощью forEach, но мне интересно задействовать функции высшего порядка Kotlin:

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 */
    }
}

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

Использование 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)

Это позволяет избежать создания списка для каждого элемента в списке, сопоставляет неправильные даты с нулем, а mapNotNull удаляет пустые значения из списка.

Использование функции расширения

Вы также можете извлечь tryOrRemove в функцию расширения, чтобы код выглядел следующим образом:

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)

Использование фильтра

Я написал его, основываясь на том, что единственными неверными датами являются н / д, что упрощает его.

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)
0 голосов
/ 09 мая 2018

Вы ищете преобразование, которое может выводить ноль или один элемент на элемент ввода. Это flatMap. Результат функции отображения должен быть Iterable, поэтому:

val dateArray = stringArray.flatMap {
    try {
        listOf(SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).parse(it))
    } catch (e: ParseException) {
        emptyList<Date>()
    }
}

Добавление следующего на основе ввода @pwolaq:

Настоятельно рекомендуется извлечь экземпляр SimpleDateFormat, поскольку он имеет тяжелую инициализацию. Кроме того, решение с mapNotNull чище, чем flatMap, я не знал об этом. Это становится особенно удобным, если вы добавляете функцию, которая, как мне кажется, отсутствует в стандартной библиотеке Kotlin:

inline fun <T> runOrNull(block: () -> T) = try {
    block()
} catch (t: Throwable) {
    null
}

С этим в вашем наборе инструментов вы можете сказать:

val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
val dateArray: List<Date> = stringArray.mapNotNull { 
    runOrNull { formatter.parse(it) } 
}
...