Nausika
Nausika

Reputation: 755

Get the next element in a list

Is there an easier way (maybe a standard function) to do this:

(If the list contains the element, the result is the next element of the list; if the element is the last element of the list, the result is the first element of the list. "Special cases": If the list is empty, the result is Nothing. If the list doesn't contain the element, the result is the first element of the list.)

import Data.List

next :: Eq a => Maybe a -> [a] -> Maybe a
next _ [] = Nothing
next Nothing (x:_) = Just x
next (Just x) list = Just $ list !! index
    where index =
        case elemIndex x list of
        Nothing -> 0
        Just xIndex ->
            if xIndex + 1 == length list
            then 0
            else xIndex + 1

Upvotes: 0

Views: 5908

Answers (3)

Rein Henrichs
Rein Henrichs

Reputation: 15605

By zipping a list with its tail, we obtain an association list of elements and their successors:

> lookup 1 $ (zip <*> tail) [1,2,3,4]
Just 2

> lookup 4 $ (zip <*> tail) [1,2,3,4]
Nothing

It is also lazy:

> lookup 4 $ (zip <*> tail) [1..]
Just 5

Upvotes: 2

gwenzek
gwenzek

Reputation: 2944

First I'd change the signature of your function, usually you don't define functions that apply only on Maybe.

So I introduce a function that returns the part of the list after the given element.

following :: (Eq a) => a -> [a] -> [a]
following _ [] = []
following x (y:l) = if x==y then l else following x l

Then I define next that returns the first element of 'following x l' or the first element of the list.

next :: (Eq a) => [a] -> a -> Maybe a
next [] _ = Nothing
next l x = if f == [] then Just(head l) else Just(head f)
    where f = following x l

Now you can use 'next' this way :

> l = 2 : 5 : 6 : 1 : []
> next l 5
6
> next l 7
2

If you want to call next on a Maybe you can use the fact that a Maybe is a Monade.

> Just 5 >>= next l
Just 6

Note: I swapped the argument of next to make it easier to call it his way.

Upvotes: 2

Zeta
Zeta

Reputation: 105955

Lets call the element e and the list xs. It gets a lot easier if you drop all elements at the start which aren't equal to e with dropWhile (/= e). Afterwards, you can focus on two cases, in which we will always return a value:

  1. The remaining list contains at least two elements. We can pattern match on (_:y:_) to get the element after e and return y
  2. The remaining list either consists of only one element or is empty. In this case, we can return the original first element.

Overall, we get

next :: Eq a => Maybe a -> [a] -> Maybe a
next _ []             = Nothing
next Nothing    (x:_) = Just x
next (Just e) l@(x:_) = Just $ case dropWhile (/= e) l of
                          (_:y:_) -> y
                          _       -> x

Upvotes: 7

Related Questions