Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map over list, except for last list element

How do I best map over all elements of a list, except for the last list element?

Say we have a list let l = [1,2,3,4] and want to get [2,3,4,4].

I do have a solution, but it doesn't feel like the "functional" way to do it (in ghci):

let l = [1,2,3,4]
let len = toIntegral $ length l -- to avoid a type mismatch Integer <-> Int
let l1 = zip l [1..]
let l2 = map (\(x,y) -> if y < len then (x + 1,y) else (x,y)) l1
let l3 = map (fst) l2

Not very nice...I do hope there is a better way! Since I'm a novice in functional programming, I don't know where to start looking for it though.

like image 355
mort Avatar asked Sep 15 '14 16:09

mort


4 Answers

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.

like image 152
bheklilr Avatar answered Oct 20 '22 14:10

bheklilr


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
like image 28
Will Ness Avatar answered Oct 20 '22 13:10

Will Ness


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
like image 34
Rvion Avatar answered Oct 20 '22 14:10

Rvion


What makes this last element so special?

I know it does not answer your question, but I'd consider a type like

data Foo a = Foo a [a]

and an appropriate Functor.

like image 1
Jean-Baptiste Potonnier Avatar answered Oct 20 '22 13:10

Jean-Baptiste Potonnier