Is there a stdlib function for something like fold...
# announcements
e
Is there a stdlib function for something like fold or reduce with a stopping condition?
Copy code
var current: Segment? = null
data
  .segments
  .find { segment ->
    shouldSegmentBeShown()
      .let { (meetsDependency, crossesFrequencyThreshold) ->
        if(meetsDependency && !crossesFrequencyThreshold) {
          current = segment
        }
        meetsDependency && crossesFrequencyThreshold
      }
}
n
You can precede your fold with takeWhile
sorry just looking at code now
Do I understand correctly, you seem to want the last element that meets one condition, in the part of the list prior to another condition being met?
It seems like you want the last element that satisfies
meetsDependency && !crossesFrequencyThreshold
, before finding something that
meetsDependency && crossesFrequencyThreshold
e
What I'm looking for is to get the first element that meets a terminating condition, but have access to the last element that met some other condition
n
Are the frequencies strictly increasing or no?
e
The frequency is just an implementation thing from my code (I just copy pasted instead of making it more generic)
n
it's easy enough to do in two passes but I'm not sure if you'll be happy with that 🙂
ah, actually I think I got it
Ugh. It's doable but it's really hard to get it all right, if I wer working at an IDE with completion etc I'd be able to do it but it's too hard in here. But this is the basic idea.
Copy code
val (current, last) = data
    .segments.asSequence()
    .map { it to shouldSegmentBeShown() }
    .fold(Pair(null, null)) { ....
    .takeWhile( ... )
    .last()
So, we first do the map because we are going to need the result of
shouldSegmentBeShown()
in two steps
Then, we do a fold. We start the fold with a pair of nulls... this fold is going to return the two things that you want. The first is going to simply be the current element, i.e. the argument to the fold. The second is going to be set to the current fold argument only if it meets the condition you used for
current = segment
Then, we do takeUntil. Here, we use the condition that you used in the
find
(negated, I think). Basically, keep processing the sequence until this condition is no longer true. And then, we take the last element. That's basically the idea. Except, takeWhile might not even be quite right, you might need takeWhileInclusive..
Bottom line is that the procedural approach is probably easier here, unless you want to write some support dataclasses or something. As it stands it's going to be really confusing as you have these pairs everywhere. Note that you can encapsulate the procedural approach in a
run
block so that the net result is still clean from the outside:
Copy code
val (current, last) = run {
....
}
Ugh, yeah, it's still not quite right, you need to keep the shouldSegmentBeShown() data through the fold, so your folded data type has to be even more complex... maybe there's a cleaner approach where you do the fold at the end, but not sure how
probably doable with takeWhileInclusive
Okay, my last attempt. Using this: https://jivimberg.io/blog/2018/06/02/implementing-takewhileinclusive-in-kotlin/
Copy code
val (current, last) = data.segments.asSequence()
    .map { it to shouldSegmentBeShown() }
    .takeWhileInclusive { (segment, shouldShow) -> 
        val (meetsDep, crossesFreq) = shouldShow
        !(meetsDep && crossesFreq)
    }.fold(Pair(null, null)) { acc, t ->
        val (segment, shouldShow) = t
        val (meetsDep, crossesFreq) = ShouldShow
        Pair( if(meetsDep && !crossesFreq) segment else acc.first, segment)
    }.last()
As you can see, gets pretty awful because of all the destructuring, I didn't even know if you were working with pairs so I didn't use first second... if you have some proper data classes with named fields this would be a lot nicer but it's still pretty dense compared to the imperative solution
it will probably need some type annotations to compile too
e
Thanks! I'm taking it in a slightly different direction, but I appreciate the effort to put that together