Mapping With An Index In PureScript
For a while I struggled to find a good solution to iterating over an array and having access to both the array element and the index.
At one time I was using this function called "mapIndex" which turned an Array a
into a Tuple Int a
.
-- | Map a set to the same set but indexed so fst is the index and snd is
-- | element of the set.
mapIndex :: forall a. Array a -> (Array (Tuple Int a))
mapIndex = snd <<< (foldl
(\(Tuple count a) val -> Tuple (count + 1) (snoc a (Tuple count val)))
(Tuple 0 [])
)
But today, I had one of those moments where the solution seemed so obvious and natural. Using mapWithIndex with the Tuple constructor, you can get the same type signature as the function above. And when used in conjunction with Data.Traversable.for, you can easily extend an existing iteration over an Array to also include the array index. To illustrate, take this example, which creates several buttons, and then in a similar fashion, creates a set of tabindexed buttons that uses the array index...
mapIndex :: forall a. Array a -> (Array (Tuple Int a))
mapIndex a = mapWithIndex Tuple a
type Action = { command :: String, display :: String }
-- An array of actions
actions :: Array Action
actions = [
{ command : "goto-playground"
, display : "Goto Playground"
},
{ command : "goto-parking-lot"
, display : "Goto Parking Lot"
}
]
-- Creates a html button for each action in the array
makeButtons :: forall e
. Array Action
-> Eff (dom :: DOM | e) (Array Element)
makeButtons arr = for arr \action -> do
button <- document >>= createElement "button"
-- set buttons display...
pure button
-- Creates a tabindexed html button for each action in
-- the array
makeButtonsIndexed :: forall e
. Array Action
-> Eff (console :: CONSOLE | e) (Array Element)
makeButtonsIndexed arr =
for (mapWithIndex Tuple arr) \(Tuple index action) -> do
button <- document >>= createElement "button"
-- set the tabindex
setAttribute "tabindex" (show index) button
-- set buttons display...
pure button