Есть ли способ объединить фильтр и карту в одну операцию в Kotlin? - PullRequest
0 голосов
/ 03 мая 2019

Код ниже будет искать "=", а затем разделять их. Если нет "=", сначала отфильтруйте их

myPairStr.asSequence()
        .filter { it.contains("=") }
        .map { it.split("=") }

Однако, видя, что у нас есть оба

        .filter { it.contains("=") }
        .map { it.split("=") }

Интересно, есть ли одна операция, которая могла бы объединить операцию вместо того, чтобы делать это отдельно?

Ответы [ 2 ]

0 голосов
/ 03 мая 2019

Я вижу вашу точку зрения, и под капотом split также проводит indexOf -проверку, чтобы получить соответствующие детали.

Я не знаю ни одной подобной функции, поддерживающей обе операции в одной, хотя такая функция в основном была бы просто аналогична той, что мы уже использовали для private fun split -осуществления.

Таким образом, если вы действительно хотите и то, и другое за один шаг (и вам требуется эта функциональность чаще), вы можете захотеть реализовать свою собственную splitOrNull -функцию, в основном копируя текущую (приватную) реализацию split и адаптируя в основном 3 его части (тип возврата List<String>?, условие, если indexOf доставляет -1, мы просто возвращаем null; и некоторые значения по умолчанию, чтобы его было легко использовать (ignoreCase=false, limit=0); помечены изменения с // added или // changed):

fun CharSequence.splitOrNull(delimiter: String, ignoreCase: Boolean = false, limit: Int = 0): List<String>? { // changed
    require(limit >= 0, { "Limit must be non-negative, but was $limit." })

    var currentOffset = 0
    var nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
    if (nextIndex == -1 || limit == 1) {
        if (currentOffset == 0 && nextIndex == -1) // added
            return null                            // added
        return listOf(this.toString())
    }

    val isLimited = limit > 0
    val result = ArrayList<String>(if (isLimited) limit.coerceAtMost(10) else 10)
    do {
        result.add(substring(currentOffset, nextIndex))
        currentOffset = nextIndex + delimiter.length
        // Do not search for next occurrence if we're reaching limit
        if (isLimited && result.size == limit - 1) break
        nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
    } while (nextIndex != -1)

    result.add(substring(currentOffset, length))
    return result
}

Имея такую ​​функцию на месте, вы можете суммировать contains / indexOf и split в один вызов:

myPairStr.asSequence()
         .mapNotNull {
           it.splitOrNull("=") // or: it.splitOrNull("=", limit = 2)
         }

В противном случае ваш нынешний подход уже достаточно хорош. Вариантом этого будет просто проверить размер разбиения после его разбиения (в основном устраняя необходимость писать contains('=') и просто проверяя ожидаемый размер, например ::

myPairStr.asSequence()
         .map { it.split('=') } 
         .filter { it.size > 1 }

Если вы хотите разделить $key=$value -форматы, где value может содержать дополнительные =, вы можете вместо этого использовать следующее:

myPairStr.asSequence()
         .map { it.split('=', limit = 2) } 
         .filter { it.size > 1 }
//       .associate { (key, value) -> key to value }
0 голосов
/ 03 мая 2019

Вы можете использовать mapNotNull вместо map.

myPairStr.asSequence().mapNotNull { it.split("=").takeIf { it.size >= 2 } }

Функция takeIf вернет null, если размер list, возвращенный методом split, равен 1 т.е. если = отсутствует в строке.И mapNotNull будет принимать только non null значения и помещать их в список (который, наконец, возвращается).В вашем случае это решение будет работать.В других сценариях реализация (для объединения filter & map) может отличаться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...