Best way to model list of values for quick updates


#1

Hi, I’m curious what’s the best way to model my data structure so it’s impossible to run into issues (make impossible states impossible) and is also performant.

I have an SVG with points { x: Int, y: Int } on it. User can click and drag those points around. I need to x and y position as user drags it’s mouse. Currently, it’s implemented as a list of points and selected point index.

Here’s the full working example https://ellie-app.com/3krKBgDJRKRa1

When user clicks I simply save index of that clicked point

SelectPoint selectedIndex ->
      { model | maybeSelectedIndex = Just selectedIndex }

When user releases mouse btn, I just set it to Nothing.

The update function is getting quite complex to my taste

        MovePoint mouseMoveData ->
            let
                updatedPoints =
                    case model.maybeSelectedIndex of
                        Just selectedPointIndex ->
                            model.points
                                |> List.indexedMap
                                    (\index point ->
                                        if index == selectedPointIndex then
                                            { x = mouseMoveData.offsetX
                                            , y = mouseMoveData.offsetY
                                            }

                                        else
                                            point
                                    )

                        Nothing ->
                            model.points
            in
            { model | points = updatedPoints }

Is there a better data structure/way to model this?

Thank you


#2

There are a couple of zipper list implementations you could look at:

https://package.elm-lang.org/packages/wernerdegroot/listzipper/latest/

Or

https://package.elm-lang.org/packages/yotamDvir/elm-pivot/latest/

Or maybe an Array where the index is the index into the array, or possibly a Dict where the index forms the key?


#3

I’ve used a Dict combined with pattern matching of a pair in this example https://ellie-app.com/3kJ7Hjv3yjNa1. It makes the code a lot simpler in my opinion, and it is normal to discard events that happen in unexpected situations with a _.

A few remarks though. Beware that chrome and Firefox have different behaviors regarding the offset values. Specifically, Firefox uses the target to compute the relative coordinates. So when your mouse moves with the point below, the coordinates are relative to the top left corner of the point instead of top left corner of the Svg element. Resulting in some kind of “blinking” where the point actually jump from correct to incorrect coordinates every other frame.

You can fix that with different techniques. Probably the more appropriate would be to use Client coordinates, and to keep in your model the coordinates of the Svg document with the new Browser.Dom apis to compute the relative coordinates yourself.


#4

Thank you guys. Zipper looks interesting, I’ve never heard about it before. Dictionary is a safe bet at the moment.

btw @mattpiz, thanks for pointing out browser differences. I will gave to study MDN documentation about SVG a bit more.