31

I got a list, and now I want the nth item. In Haskell I would use !!, but I can't find an elm variant of that.

glennsl
  • 28,186
  • 12
  • 57
  • 75
11684
  • 7,356
  • 12
  • 48
  • 71

3 Answers3

41

Elm added arrays in 0.12.1, and the implementation was massively overhauled in 0.19 to improve correctness and performance.

import Array

myArray = Array.fromList [1..5]

myItem = Array.get 2 myArray

Arrays are zero-indexed. Negative indices are not supported currently (bummer, I know).

Note that myItem : Maybe Int. Elm does everything it can to avoid runtime errors, so out of bounds access returns an explicit Nothing.

If you find yourself looking to index into a list rather than take the head and tail, you should consider using an array.

Array documentation

mgold
  • 6,189
  • 4
  • 39
  • 41
  • 1
    `negativeGet index array = get (index + length array) array` – Alex Shroyer Mar 08 '16 at 16:18
  • Does the last line still hold true in light of https://github.com/elm-lang/core/issues/649 ? – KillerX Sep 29 '17 at 08:22
  • The fixed array implementation has been merged but not released. – mgold Oct 08 '17 at 21:02
  • Confirm. Latest release 5.1.1 is from 2017-01-23 https://github.com/elm-lang/core/releases and Skinney commited on 2017-04-02 https://github.com/elm-lang/core/commit/4213c610b3ecb4b856e09b6b07577d06c0e3dfef#diff-67a364ff7fde87ebc2f09ac1c9ad0680 – rofrol May 03 '18 at 09:23
  • You could make this support both positive and negative indices by `mod`ing the index by the length of the array. –  Apr 17 '21 at 15:48
14

There is no equivalent of this in Elm. You could of course implement it yourself.

(Note: This is not a "total" function, so it creates an exception when the index is out of range).

infixl 9 !!
(!!) : [a] -> Int -> a
xs !! n  = head (drop n xs)

A better way would be to define a total function, using the Maybe data type.

infixl 9 !!
(!!) : [a] -> Int -> Maybe a
xs !! n  = 
  if | n < 0     -> Nothing
     | otherwise -> case (xs,n) of
         ([],_)    -> Nothing
         (x::xs,0) -> Just x
         (_::xs,n) -> xs !! (n-1)
Daniël Heres
  • 243
  • 2
  • 4
  • Just to add another alternative (last (take index list) -> works only if index is not 0 or 1 but you get the idea. – Jeremy D Apr 27 '14 at 10:53
  • Haskell raises an out of range exception when you use (!!) out of range. I guess it would make sense to use Maybe but it seems odd to request an element from a list and get something of a type that's not the type of the elements in the list (in this case getting Maybe a instead of a) – Hassan Hayat Apr 29 '14 at 22:53
  • 11
    In more recent versions of Elm, you can define `get n xs = List.head (List.drop n xs)` and it's a total function, done. The `|` syntax above was removed in 0.16. – mgold Dec 13 '15 at 19:42
2

I've used this:

(!!): Int -> List a -> Maybe a

(!!) index list =                          -- 3 [ 1, 2, 3, 4, 5, 6 ]

   if  (List.length list) >= index then

        List.take index list               -- [ 1, 2, 3 ]
        |> List.reverse                    -- [ 3, 2, 1 ]
        |> List.head                       -- Just 3
   else 
      Nothing

Of course you get a Maybe and you need to unwrap it when you use this function. There is not guarantee that your list will not be empty, or that you ask for a imposible index (like 1000) - so that's why elm compiler forces you to account for that case.

main = 
let 
  fifthElement = 
    case 5 !! [1,2,3,4,255,6] of  // not sure how would you use it in Haskell?! But look's nice as infix function. (inspired by @Daniël Heres)
      Just a ->
        a
      Nothing ->
        -1
in
    div [] 
        [ text <| toString fifthElement ]         // 255
AIon
  • 12,521
  • 10
  • 47
  • 73