Map over list, except for last list element

Just re-write map but make a special case when there's only one element:

mapBut1 :: (a -> a) -> [a] -> [a]
mapBut1 f [] = []
mapBut1 f [x] = [x]
mapBut1 f (x:xs) = f x : mapBut1 f xs

This will now work even for infinite lists, it's a lot faster than calculating the length, and makes it more readable. Note that this does restrict your function to be of type a -> a instead of a -> b.

Alternatively, you could do

mapBut1 f (x:y:xs) = f x : mapBut1 f (y:xs)
mapBut1 f other = other

They're equivalent definitions, but the latter uses 1 fewer pattern matches. I would prefer the former, though, since it's more immediately obvious what cases are being handled.


This is a job for pretend-paramorphism, as usual:

import Data.List (tails)

mapButLast :: (a -> a) -> [a] -> [a]
mapButLast f = foldr g [] . tails where
  g (x:_:_) r = f x : r
  g xs      _ = xs

Or with proper para we'd just write

mapButLast f = para g [] where
  g x [] r = [x]
  g x _  r = f x : r

where

para f z (x:xs) = f x xs (para f z xs)
para f z []     = z

You can also use init and last from standard librairies (no special import) if you don't care about performances.

you can write

map f (init list) ++ [last list]

where

  • f : function you want to map
  • list : list you want to map over