Is there a mix between `map` and `fold` where I ca...
# getting-started
h
Is there a mix between
map
and
fold
where I carry over an accumulated value from iteration to iteration, but transform the list? So that a transformed iterable is returned and not the accumulated value?
y
There's
runningFold
I think it's called which gives you all the accumulated values. But likely what you want is just a mutable variable that you write the values into
a
A pure solution would be to have the mapped list and the accumulator to be carried across the fold:
Copy code
val (_, mappedList) = list.fold(Pair(initialAcc(), emptyList())) { (acc, mappedList), value ->
  val newAcc = computeAcc(acc, mappedList, value)
  val newMappedList = mappedList + map(acc, value)
  Pair(newAcc, newMappedList)
}
m
not sure if I understand it correctly, but scan might be useful
h
fold
and
scan
return the accumulated value(s) not the transformed underlying iterable. Aron's snipped looks promising, I'll check it out, thank you.
e
that's a terrible idea for performance - adding to a list normally is O(1) amortized, but O(n) when creating an immutable copy
if you really want to do that, use persistent lists a la kotlinx.collectio s.immutable
but why not just use scan/runningFold, you get the same list in the end
it may just require an extra transformation
y
My understanding is that you want something like:
Copy code
inline fun <T, R, S> Iterable<T>.mapWithState(initial: S, transform: (S, T) -> Pair<S, R>): List<R>
but that can be implemented simply as:
Copy code
{
  var state = initial
  return map { element -> 
    val (newState, transformed) = transform(state, element)
    state = newState
    transformed
  }
}
So you should just use mutable state instead
h
Oh sorry, I didn't look again after implementing it. I ended up just copying fold and adjusting it to have a mutable iterable.
Copy code
public inline fun <T, R> Iterable<T>.foldMap(initial: Pair<R, Iterable<T>>, operation: (acc: R, T) -> Pair<R, T>): Iterable<T> {
    var (accumulatorValue, accumulatorIterable) = initial
    for (element in this) {
        val (value, iterable) = operation(accumulatorValue, element)
        accumulatorValue = value
        accumulatorIterable += iterable
    }
    return accumulatorIterable
}
@ephemient I need the accumulated value to transform the element in the list where I currently am while stepping through. The alternative would have been to have a
map
inside of a for loop. I just wanted to see/ask if there is something out of the box
y
This can be made more performant by doing:
Copy code
public inline fun <T, R> Iterable<T>.foldMap(initial: Pair<R, Iterable<T>>, operation: (acc: R, T) -> Pair<R, T>): List<T> = buildList(initial.second.size) {
    var (accumulatorValue, accumulatorIterable) = initial
    addAll(accumulatorIterable)
    for (element in this@foldMap) {
        val (value, iterable) = operation(accumulatorValue, element)
        accumulatorValue = value
        add(iterable)
    }
}
h
Oh, cool thank you. I think I also could have just a Pair with my accumulator value and a mutableList. But I like this more.
y
But you also really don't need to pass that initial prefix of your iterable, so I would personally simplify this to:
Copy code
public inline fun <T, R> Iterable<T>.foldMap(initial: R, operation: (acc: R, T) -> Pair<R, T>): List<T> = buildList(size) {
    var accumulatorValue = initial
    for (element in this@foldMap) {
        val (value, iterable) = operation(accumulatorValue, element)
        accumulatorValue = value
        add(iterable)
    }
}
// Usage
myPrefix + iterable.foldMap(...)
And then, the implementation can get more succinct:
Copy code
public inline fun <T, R> Iterable<T>.foldMap(initial: R, operation: (acc: R, T) -> Pair<R, T>): List<T> {
    var accumulatorValue = initial
    return map { element ->
        val (value, iterable) = operation(accumulatorValue, element)
        accumulatorValue = value
        iterable
    }
}
which becomes simply what I presented above
h
I didn't know about buildList, thank you. But
Iterable
doesn't provide a
size
for it.
y
And then it's very clear that you can just use a mutable variable on the outside instead of juggling `Pair`s around And then it's very clear that instead of using
pairs
Oops yes I didn't test the code, you can just delete the
size
though
Note btw that
foldMap
has a completely different meaning elsewhere. It's used as a
map
then a
fold
h
Yeah it was a placeholder name, I already guessed it could have collisions with flowables or something.
which becomes simply what I presented above
And like this the circle closes (German proverb)