Как лучше всего найти элемент в вложенных списках?

Kotlin предоставляет некоторые полезные функции расширения, позволяющие потоковое программирование.

Например, если я ищу элемент в списке, я могу использовать find :

 return list.find { n -> n>4 && n<6 } 

Но когда у меня есть вложенные списки, это кажется мне непрактичным. Тогда я использую forEach – к счастью, я могу вернуться из внутренней Лямбды с Котлином:

 private fun findUsingForEach(data: List<List<Int>>, pred : (Int) -> Boolean) : Optional<Int> { data.forEach { list -> list.forEach { n -> if( pred(n) ) return Optional.of(n) } } return Optional.empty() } 

Мне кажется, что forEach не подходит для этого. Есть ли более функциональный способ этого? filter приходит на ум, но вложенность вызывает проблемы.

Этот результат – это тест, который я использую для функции abouve:

 @Test open fun findTest() { val data = listOf( listOf(1,2,3), listOf(3,4,5,6), listOf(), listOf(6,7,8) ) val e = findUsingForEach( data, { n -> n>4 && n < 6 } ) assertEquals(5, e.get()) } 

Вы можете flatten список:

fun <T> Iterable<Iterable<T>>.flatten(): List<T> ( source )

Возвращает единственный список всех элементов из всех коллекций в данной коллекции.

 val data = listOf(listOf(1, 2, 3), listOf(3, 4, 5, 6), listOf(), listOf(6, 7, 8)) data.flatten().find { n -> n > 4 && n < 6 } 

Это вернет единый список с элементами подписок в порядке. Затем вы можете использовать find как обычно.

В вашем примере,

 {{1, 2, 3}, {3, 4, 5, 6}, {}, {6, 7, 8}} 

становится

 {1, 2, 3, 3, 4, 5, 6, 6, 7, 8} 

и результат find в этом списке равен 5 .

Однако это создаст новый список. Взгляните на источник flatten :

 /** * Returns a single list of all elements from all collections in the given collection. */ public fun <T> Iterable<Iterable<T>>.flatten(): List<T> { val result = ArrayList<T>() for (element in this) { result.addAll(element) } return result } 

Если вы хотите сохранить память, сначала создайте Sequence из списка:

 data.asSequence() 

а затем выполните следующие действия:

 data.asSequence().flatten().find { n -> n > 4 && n < 6 } 

Боковое примечание: ваш предикат n > 4 && n < 6 , просто эквивалентен n == 5 .

Если вы просто хотите уменьшить количество кодов, и вы не очень заботитесь об эффективности, попробуйте это.

 list.flatten().find { your pred here } 

Или

 list.flatMap { it }.find { your pred } 

Или создайте полезную утилиту, которая не создает новые списки (более быстрая / более низкая память):

 inline fun <T> Iterable<Iterable<T>>.forEachEach(f: (T) -> Unit) = forEach { it.forEach(f) }