We have a detekt rule against nested block depth. ...
# getting-started
c
We have a detekt rule against nested block depth. Rather than surpressing it I want to maybe simplify some things. I'm wondering if two inner ForEach statements can be simplified into 1 somehow? e.g.
Copy code
outerList.forEach { outerItem ->
    innerList.forEach { innterItem ->
s
Using
flatMap
on a
Sequence
is one general pattern for expressing this sort of nested loop without the nesting
Copy code
outerList.asSequence().flatMap { innerList.asSequence() }.forEach { ... }
Not sure it's exactly simpler, though...
l
How deep is this? I'd be looking if I can extract anything higher to a method first.
c
Detekt complains at 4. i basically have two forEach loops, and then two nested ifs
so full method looks like this essentially
Copy code
outerList.forEach { outerItem ->
  innerList.forEach { innerItem ->
    if (outerItem.blah()) {
      set.put(outerItem)
      if (innerItem.foo()) { return "something" }
    }
so my inner if's could also be improved. not sure how honestly
l
This loop looks interesting. Can the outer item if be moved outside of the inner for loop? That prevents inserting it to the set for every inner item (it's a set so no duplicates, but still). It's hard to tell from an abstract view like this, but it almost doesn't seem like what you need here is a 'for each combination'. I'm making some assumptions like that blah is stateless or that set is indeed a set and not a list.
c
yeah. its kinda annoying. but as far as i can see it has to stay this way.
l
Are the outer item and inner item related in any way such that you can flatten this to a single list. It seems like what you care about is inner items that match a condition foo (specifically the first) when outer items match a condition blah. I don't see any clear relationship between them here in this code, though. Can the set just be
outerList.filter { it.blah() }
and the return value be
innerList.first { it.foo() }
? Unless there's a relationship between them that was omitted for brevity, you may not need the nested loop.
I remember seeing something in the advent of code videos. I believe my solution was to make a 'forEachCombination(list1: List<T>, list2: List<R>, block: (T, R) -> Unit)'. You could reduce this to 3 layers by using that. It's basically just a function that does the two for loops and calls block. The Jetbrains video did something similar, but more elegant if I remember correctly.
j
for(outerItem in outerList.filter{it.blah()}){}
k
Sam's suggestion:
outerList.asSequence().flatMap { innerList.asSequence() }.forEach { ... }
unfortunately just produces
outerList.size
copies of
innerList
items. To get the actual Cartesian product, you need something like this:
Copy code
outerList.asSequence().flatMap { outer ->
    innerList.asSequence().map { inner ->
        outer to inner
    }
}
    .forEach { (outer, inner) ->
        ... 
    }
which unfortunately doesn't save you any indent depth.
😄 1
e
how about this? scales to any number of lists, as long as the block is idempotent before all items are chosen
Copy code
chooseAll {
    val x = choose(0, 3, 6)
    val y = choose(0, 1, 2)
    println("$x + $y = ${x + y}")
}
Copy code
0 + 0 = 0
0 + 1 = 1
0 + 2 = 2
3 + 0 = 3
3 + 1 = 4
3 + 2 = 5
6 + 0 = 6
6 + 1 = 7
6 + 2 = 8
(inspired by wanting to be able to use something like Haskell's
Copy code
sequence_ $ do
    x <- [0, 3, 6]
    y <- [0, 1, 2]
    pure $ printf "%d + %d = %d\n" x y (x + y)
in Kotlin… unfortunately can't statically guarantee the lack of side-effects in Kotlin, unlike Haskell)
actually I forgot about the fact that Scala has it built-in,
Copy code
for {
    x <- 0 to 6 by 3
    y <- 0 to 2
} println(s"$x + $y = ${x + y}")
I wonder if anyone has proposed adding similar to Kotlin
362 Views