diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+## 0.5.2.0
+
+- Added map(), upper(), lower() functions
+
+## 0.5.1.3
+
+- Documentation fixes
+
+## 0.5.1.2
+
+- Release-related fixups
+
 ## 0.5.1.1
 
 - Bugfixes wrt indentation mode
diff --git a/ginger.cabal b/ginger.cabal
--- a/ginger.cabal
+++ b/ginger.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                ginger
-version:             0.5.1.3
+version:             0.5.2.1
 synopsis:            An implementation of the Jinja2 template language in Haskell
 description:         Ginger is Jinja, minus the most blatant pythonisms. Wants
                      to be feature complete, but isn't quite there yet.
diff --git a/src/Text/Ginger/Run.hs b/src/Text/Ginger/Run.hs
--- a/src/Text/Ginger/Run.hs
+++ b/src/Text/Ginger/Run.hs
@@ -138,6 +138,7 @@
     , ("length", fromFunction . unaryFunc $ toGVal . length)
     , ("less", fromFunction gfnLess)
     , ("lessEquals", fromFunction gfnLessEquals)
+    , ("map", fromFunction gfnMap)
     , ("modulo", fromFunction . variadicNumericFunc 1 $ fromIntegral . modulo . Prelude.map Prelude.floor)
     , ("nequals", fromFunction gfnNEquals)
     , ("num", fromFunction . unaryFunc $ toGVal . asNumber)
@@ -153,6 +154,8 @@
     , ("sum", fromFunction . variadicNumericFunc 0 $ Prelude.sum)
     , ("truncate", fromFunction . unaryNumericFunc 0 $ Prelude.fromIntegral . Prelude.truncate)
     , ("urlencode", fromFunction gfnUrlEncode)
+    , ("upper", fromFunction . variadicStringFunc $ mconcat . Prelude.map Text.toUpper)
+    , ("lower", fromFunction . variadicStringFunc $ mconcat . Prelude.map Text.toLower)
     ]
 
 -- | Simplified interface to render a ginger template \"into\" a monad.
diff --git a/src/Text/Ginger/Run/Builtins.hs b/src/Text/Ginger/Run/Builtins.hs
--- a/src/Text/Ginger/Run/Builtins.hs
+++ b/src/Text/Ginger/Run/Builtins.hs
@@ -260,6 +260,39 @@
             return . toGVal $ Text.replace search replace str
         _ -> fail "Invalid arguments to 'replace'"
 
+gfnMap :: Monad m => Function (Run m h)
+gfnMap args = do
+    let parsedArgs = extractArgsDefL
+            [ ("collection", def)
+            , ("function", def)
+            , ("attribute", def)
+            ]
+            args
+    (dictMay, listMay, functionMay, attributeMay) <- case parsedArgs of
+        Right [collection, function, attribute] ->
+            return ( asDictItems collection
+                   , asList collection
+                   , asFunction function
+                   , Just (asText attribute)
+                   )
+        _ ->
+            fail "Invalid args to map()"
+    mapFunction <- case (functionMay, attributeMay) of
+        (Just f, _) -> return f
+        (Nothing, Just key) -> return $ \case
+            (_, item):_ ->
+                return $ lookupLooseDef def (toGVal key) item
+            _ -> 
+                return def
+        _ -> fail "You have to pass a function or an attribute"
+    case (dictMay, listMay) of
+        (Just items, _) ->
+            dict <$> forM items
+                (\(key, value) -> (key,) <$> mapFunction [(Nothing, value)])
+        (Nothing, Just items) ->
+            toGVal <$> mapM (mapFunction . (:[]) . (Nothing,)) items
+            
+
 gfnSort :: Monad m => Function (Run m h)
 gfnSort args = do
     let parsedArgs = extractArgsDefL
diff --git a/test/Text/Ginger/SimulationTests.hs b/test/Text/Ginger/SimulationTests.hs
--- a/test/Text/Ginger/SimulationTests.hs
+++ b/test/Text/Ginger/SimulationTests.hs
@@ -283,6 +283,14 @@
             mkTestHtml [] []
                 "{{ \"this is the end of the world\"|capitalize }}"
                 "This is the end of the world"
+        , testCase "\"upper\"" $ do
+            mkTestHtml [] []
+                "{{ \"this is the end of the world\"|upper }}"
+                "THIS IS THE END OF THE WORLD"
+        , testCase "\"lower\"" $ do
+            mkTestHtml [] []
+                "{{ \"This is the END OF THE WORLD\"|lower }}"
+                "this is the end of the world"
         , testGroup "\"center\""
             [ testCase "extra space" $ do
                 mkTestHtml [] []
@@ -467,6 +475,20 @@
                 mkTestHtml [] []
                     "{{ \"%s - %s\"|format('Hello?', 'Foo!') }}"
                     "Hello? - Foo!"
+            ]
+        , testGroup "\"map\""
+            [ testCase "map function over list" $ do
+                mkTestHtml [] []
+                    "{{ map(['FOO', 'BAR'], lower) }}"
+                    "foobar"
+            , testCase "map function over dictionary" $ do
+                mkTestHtml [] []
+                    "{{ map({'foo': 'bar'}, upper)['foo'] }}"
+                    "BAR"
+            , testCase "map to extract attribute" $ do
+                mkTestHtml [] []
+                    "{{ map([{'name':'foo'}, {'name': 'bar'}], attribute='name') }}"
+                    "foobar"
             ]
         , testGroup "\"not-equals\""
             [ testCase "all equal" $ do
