Search code examples
arrayslistelm

Better way to move an Elm list element


I have a List in my Elm model. On a Drag message containing the previous index and next index (both zero-based) of an element, I want to move the element. This is the code that I am using now. I first convert to an Array, then use slices to rearrange the array, finally converting back to a List. Is there a better way (maybe not involving arrays). Note that the list will never have more than 5 items.

Drag { prev, next } ->
    let
        arr =
            Array.fromList model.list

        temp =
            Array.append
                (Array.slice 0 prev arr)
                (Array.slice (prev + 1) (Array.length arr) arr)

        list_ =
            Array.toList <|
                Array.append
                    (Array.append
                        (Array.slice 0 next temp)
                        (Array.slice prev (prev + 1) arr)
                    )
                    (Array.slice next (Array.length temp) temp)

        model_ =
            { model | list = list_ }
    in
    ( model_, Cmd.none )

Solution

  • If you find yourself using Array.slice a lot, it's worth looking at the List.take and List.drop functions to see if they can do what you need!

    For your example, it can be rewritten using List.take, List.drop, and List.concat:

    Drag { prev, next } ->
        let
            item =
                List.drop prev model.list
                    |> List.take 1
    
            rest =
                List.concat
                    [ List.take prev model.list
                    , List.drop (prev + 1) model.list
                    ]
    
            reorderedList =
                List.concat
                    [ List.take next rest
                    , item
                    , List.drop next rest
                    ]
    
            updatedModel =
                { model | list = reorderedList }
        in
            ( updatedModel, Cmd.none )
    

    I threw together a quick demo in Ellie that you can compare the two versions in.