我经常偶然发现这个问题,但是看不到一个通用的实现:我如何惯用地(从功能上)找到一个元素,在比赛之后停止搜索,还返回一个不同的类型(即,将匹配的任何类型映射到另一种类型) ?
我已经能够解决
fun <F,T> Sequence<F>.mapFirst(block: (F) -> T?): T? =
fold(AtomicReference<T>()) { ref, from ->
if (ref.get() != null) return@fold ref
ref.set(block(from))
ref
}.get()
fun main() {
Files.list(someDir).asSequence().map { it.toFile() }.mapFirst { file ->
file.useLines { lines ->
lines.mapFirst { line ->
if (line == "123") line.toInt() else null
}
}
}?.let { num ->
println("num is $num") // will print 123 as an Int
} ?: println("not a single file had a line eq to '123'")
}
但这并不会在比赛中停止(block()
返回非null时)会消耗掉所有文件及其所有行。
答案 0 :(得分:1)
一个简单的for
循环足以实现mapFirst
:
fun <F,T> Sequence<F>.mapFirst(block: (F) -> T?): T? {
for (e in this) {
block(e)?.let { return it }
}
return null
}
如果您需要一种解决方案而又不引入自己的扩展名(尽管没有任何问题),则可以使用mapNotNull
+ firstOrNull
组合:
files.asSequence()
.mapNotNull { /* read the first line and return not null if it's ok */ }
.firstOrNull()
答案 1 :(得分:0)
我不会映射然后丢弃的值,而是这样做:
sequenceOf(1, 2, 3)
.firstOrNull() { it == 2 }
?.let { it * 2 } ?: 6
首先,找到与您的条件匹配的值,然后根据需要对其进行转换。如果找不到匹配的元素,则分配一个默认值(在本例中为6)。