How to get a given element in list by id and the previous and next element?

Hi there !

I am having trouble solving a quite simple problem in Elm. I am pretty new to the functional paradigm so there might be a “good” way to solve this.

Given a List of Image. Each Image has quite a few properties but also an id.
I would like to access an image given an Id and get the previous and following element.

What would be the best way to handle this situation ? Should I use a Array instead of a List?

Thanks !

I think using List.Extra (https://package.elm-lang.org/packages/elm-community/list-extra/8.2.3/List-Extra) you would be able to do what you want, in particular with find and getAt.

To access neighboring elements of a list, you need to use tail recursion. Here’s a solution using that:

findWithNeighbors : (a -> Bool) -> List a -> ( Maybe a, Maybe a, Maybe a )
findWithNeighbors pred list =
    case list of
        a :: b :: c :: rest ->
            if pred b then
                ( Just a, Just b, Just c )

            else if pred a then
                ( Nothing, Just a, Just b )

            else
                findWithNeighbors pred (b :: c :: rest)

        a :: b :: [] ->
            if pred a then
                ( Nothing, Just a, Just b )

            else if pred b then
                ( Just a, Just b, Nothing )

            else
                findWithNeighbors pred [ b ]

        a :: [] ->
            if pred a then
                ( Nothing, Just a, Nothing )

            else
                ( Nothing, Nothing, Nothing )

        [] ->
            ( Nothing, Nothing, Nothing )

Usage:

findWithNeighbors (\i -> i == 1) [1,2,3] -- (Nothing, Just 1, Just 2)
findWithNeighbors (\i -> i == 2) [1,2,3] -- (Just 1, Just 2, Just 3)
findWithNeighbors (\i -> i == 3) [1,2,3] -- (Just 2, Just 3, Nothing)

Alternatively you could use List.Extra.findIndex and then call List.Extra.getAt three times, with index - 1 and index + 1, although it’d be less efficient.

4 Likes

Oh nice, I didn’t know about this package. I’ll definitely check it out ! Thanks !

I will give this solution a try. The list might contain a large number of elements (up to tens of thousands) so I will need to compare both solutions for different given ranges.

Thanks a lot !

Really nice solution!!!

I’m not sure this is relevant for your case:

I would be tempted to make my model reflect this kind of requirement instead of implementing a function to infer the current, prev, and next images. At least it seems to me that such a requirement would be better met by modelling it explicitly.

I would do something like Richard Feldman recommends here https://www.youtube.com/watch?v=x1FU3e0sT1I

Maybe it would look like this

type ImageModel =
    { previous = List Image
    , current = (Maybe Image, Maybe Image, Maybe Image)
    , next = List Image
    }

Depending on your use case you could even go further and maintain something like this for even more explicitness.

type ImageModel
    = NoImages
    | OnlyTwo ( Image, Image )
    | OnlyThree ( Image, Image, Image )
    | More
        { prev : List Image
        , current : ( Image, Image, Image )
        , next : List Image
        }

I have often profited from maintaining very explicit models like this rather than having a simple model with complex functions extracting the data I needed from it. The guarantees and explicitness in such models are quite useful.

3 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.