Reputation: 60081
The below code will look for "="
and then split them. If there's no "="
, filter them away first
myPairStr.asSequence()
.filter { it.contains("=") }
.map { it.split("=") }
However seeing that we have both
.filter { it.contains("=") }
.map { it.split("=") }
Wonder if there's a single operation that could combine the operation instead of doing it separately?
Upvotes: 11
Views: 6954
Reputation: 23242
I see your point and under the hood split
is also doing an indexOf
-check to get the appropriate parts.
I do not know of any such function supporting both operations in a single one, even though such a function would basically just be similar to what we have already for the private fun split
-implementation.
So if you really want both in one step (and require that functionality more often), you may want to implement your own splitOrNull
-function, basically copying the current (private) split
-implementation and adapting mainly 3 parts of it (the return type List<String>?
, a condition if indexOf
delivers a -1
, we just return null
; and some default values to make it easily usable (ignoreCase=false
, limit=0
); marked the changes with // added
or // 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
}
Having such a function in place you can then summarize both, the contains
/indexOf
and the split
, into one call:
myPairStr.asSequence()
.mapNotNull {
it.splitOrNull("=") // or: it.splitOrNull("=", limit = 2)
}
Otherwise your current approach is already good enough. A variation of it would just be to check the size of the split after splitting it (basically removing the need to write contains('=')
and just checking the expected size, e.g.:
myPairStr.asSequence()
.map { it.split('=') }
.filter { it.size > 1 }
If you want to split a $key=$value
-formats, where value
actually could contain additional =
, you may want to use the following instead:
myPairStr.asSequence()
.map { it.split('=', limit = 2) }
.filter { it.size > 1 }
// .associate { (key, value) -> key to value }
Upvotes: 1
Reputation: 908
You can use mapNotNull
instead of map
.
myPairStr.asSequence().mapNotNull { it.split("=").takeIf { it.size >= 2 } }
The takeIf
function will return null
if the size of the list
returned by split
method is 1 i.e. if =
is not present in the string. And mapNotNull
will take only non null
values and put them in the list(which is finally returned).
In your case, this solution will work. In other scenarios, the implementation(to merge filter
& map
) may be different.
Upvotes: 10