https://kotlinlang.org logo
Title
a

AdalPari

12/12/2019, 2:49 PM
Hi there! Are there any functional fun to iterate over a list and apply an operation to the current element with the previous one? It would be like a fold, but instead of accumulating the result, just applying it to each element. E.g:
(1..9).fold("a", { last, current -> last + current })
This just returns a123456789 but I actually need to create the following list: { "a1", "a12", "a123", "a1234", .. } I would be more like a map but knowing previous element:
(1..9).map { "$previousElement$it" }
s

streetsofboston

12/12/2019, 2:52 PM
Not sure if this fun exists on a List, but have you tried
scan
? (this fun exists on Rx)
a

AdalPari

12/12/2019, 2:54 PM
Not really, will check!
k

Kroppeb

12/12/2019, 3:47 PM
This is a scan, which doesn't exist in the stdlib sadly
h

Hampus Londögård

12/12/2019, 4:15 PM
You can use
windowed
.
(1..9).windowed(size=2).map { items -> "${items.first()}${items.last()}" }
It defaults to
partialWindows=false
. Not really what you asked for though, but it does the job 🙂
k

Kroppeb

12/12/2019, 4:17 PM
I don't see how you could use it.
h

Hampus Londögård

12/12/2019, 4:17 PM
Nvm, I got it wrong. Thought it was just the previous one
k

Kroppeb

12/12/2019, 4:20 PM
var previous = "a"
(1..9).map {current -> 
    previous += current
    previous
})
h

Hampus Londögård

12/12/2019, 4:22 PM
This works
(1..9).fold(listOf<String>()) { acc, current -> acc + ((acc.lastOrNull() ?: "") + current) }
👍 1
s

streetsofboston

12/12/2019, 4:22 PM
^^^ @Kroppeb That’ll work for ordered and indexed collections, but you are using a side-effect by updating an external `var`….
☝️ 1
k

Kroppeb

12/12/2019, 4:26 PM
This is the
scan
implementation I have in my utils for AoC. It doesn't return the start value. If you want the start value to also be returned you can change line 3 to
val ret = mutableListOf(start)
👍 3
s

streetsofboston

12/12/2019, 4:52 PM
Here’s another version of
scan
, which can handle large collections and scans the in- and out-put values one at a time, avoiding the allocation of possibly large collections until the end.
fun <T, R> Sequence<T>.scan(start: R, oper: (R, T) -> R): Sequence<R> = sequence {
    var next = start
    for (value in iterator()) {
        next = oper(next, value).also { yield(it) }
    }
}

fun <T, R> Iterable<T>.scan(start: R, oper: (R, T) -> R): Iterable<R> =
    this.asSequence().scan(start, oper).asIterable()
It uses a
Sequence
to do so.
a

AdalPari

12/12/2019, 5:05 PM
Lot of answers! thanks!