elm-community / list-extra

Convenience functions for working with List.
http://package.elm-lang.org/packages/elm-community/list-extra/latest
MIT License
135 stars 59 forks source link

Add mapAccuml and mapAccumr #88

Closed ThomasWeiser closed 6 years ago

ThomasWeiser commented 6 years ago

Adding two functions mapAccuml and mapAccumr that behave like a combination of map and foldl: Pass an accumulator through the list and map the elements using the accumulator value at that point.

See this blog post for nice diagrams comparing mapAccuml with map, foldl and scanl.

I took the names from the Haskell library, but with a lowercase l/r at the end, following the Elm library folklore.


One use case is using a running total of some sort when mapping the elements of a list.

A specific application of that use case that I stumbled upon are view functions, that use CSS grid-layout with items spanning across multiple cells. Say you have a model of three items, each with a specific column-span count:

items =
    [ { data: data1, span: 3 }
    , { data: data2, span: 2 }
    , { data: data3, span: 3 } 
    ]

The CSS grid spec demands that each item specifies the column number where it should start. If we adorn the list with that information it may look like this:

itemsWithStartColumn =
    [ { data: data1, span: 3, start: 1 }
    , { data: data2, span: 2, start: 4 }
    , { data: data3, span: 3, start: 6 } 
    ]

In other words, we need a running total for counting the columns already used. That's a perfect job for mapAccuml:

itemsWithStartColumn =
    mapAccuml
        (\position { data, span } ->
            ( position + span
            , { data = data, span = span, start = position }
            )
        )
        1
        items
    |> Tuple.second
Chadtech commented 6 years ago

Hey @ThomasWeiser !

I think this is great. My only piece of feedback is that maybe there could be more examples. The functions are kind of tricky and its hard to figure out what they do by reading the one example in each description.

ThomasWeiser commented 6 years ago

@Chadtech Thanks for the feedback!

I've added a diagram visualizing the operation and a second example to each of the two new functions.