diff --git a/examples/maps.out b/examples/maps.out
index 42c1b13adb78943c072c74fe300b82667c2315a9..b6fd1d002150debb221a9e3843566906726f9d48 100644
--- a/examples/maps.out
+++ b/examples/maps.out
@@ -6,9 +6,9 @@ three
 2
 1 2 3 4 5
 6 7 8
-one three
-one two three
+key1 one key3 three
+key1 one key2 two key3 three
 one
 two
 three
-1.0 2.0 3.0
+ichi 1.0 ni 2.0 san 3.0
diff --git a/src/lib/lib.rs b/src/lib/lib.rs
index b37cf175329bd190043cbaa395f78d25b7c1e086..1d63a2ade68852946b7199c606cbd915ccf5929d 100644
--- a/src/lib/lib.rs
+++ b/src/lib/lib.rs
@@ -1,5 +1,4 @@
 #![allow(unknown_lints)]
-#![allow(while_let_on_iterator)]
 
 #[macro_use]
 extern crate bitflags;
diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs
index 83ab4c5358fd7ce5867ecfa448ab63290e33523a..12ccb77efb4ad04d6ae47820fcf4b759488476e7 100644
--- a/src/lib/parser/mod.rs
+++ b/src/lib/parser/mod.rs
@@ -8,7 +8,7 @@ mod statement;
 pub use self::quotes::Terminator;
 pub(crate) use self::{
     loops::ForValueExpression,
-    shell_expand::{expand_string, Expander, Select},
+    shell_expand::{expand_string, Expander, MapKeyIter, MapValueIter, Select},
     statement::{parse_and_validate, StatementSplitter},
 };
 
diff --git a/src/lib/parser/shell_expand/mod.rs b/src/lib/parser/shell_expand/mod.rs
index a12df51b6cffc661a6bf9e62a9ef3476331917bd..2104d20de954b27eb7b28255ef6478aa5c3012b9 100644
--- a/src/lib/parser/shell_expand/mod.rs
+++ b/src/lib/parser/shell_expand/mod.rs
@@ -23,16 +23,25 @@ pub(crate) fn is_expression(s: &str) -> bool {
         || s.starts_with('\'')
 }
 
+pub type MapKeyIter<'a> = Box<dyn Iterator<Item = &'a types::Str> + 'a>;
+pub type MapValueIter<'a> = Box<dyn Iterator<Item = types::Str> + 'a>;
+
+// TODO: Make array expansions iterators instead of arrays.
+// TODO: Use Cow<'a, types::Str> for hashmap values.
 /// Trait representing different elements of string expansion
 pub(crate) trait Expander {
-    /// Expand a tilde form to the correct directory
+    /// Expand a tilde form to the correct directory.
     fn tilde(&self, &str) -> Option<String> { None }
-    /// Expand an array variable with some selection
+    /// Expand an array variable with some selection.
     fn array(&self, &str, Select) -> Option<types::Array> { None }
-    /// Expand a string variable given if its quoted / unquoted
+    /// Expand a string variable given if it's quoted / unquoted
     fn string(&self, &str, bool) -> Option<types::Str> { None }
-    /// Expand a subshell expression
+    /// Expand a subshell expression.
     fn command(&self, &str) -> Option<types::Str> { None }
+    /// Iterating upon key-value maps.
+    fn map_keys<'a>(&'a self, &str, Select) -> Option<MapKeyIter> { None }
+    /// Iterating upon key-value maps.
+    fn map_values<'a>(&'a self, &str, Select) -> Option<MapValueIter> { None }
 }
 
 fn expand_process<E: Expander>(
diff --git a/src/lib/parser/shell_expand/words/methods/arrays.rs b/src/lib/parser/shell_expand/words/methods/arrays.rs
index 0e7269da930fbb6d0e4d13bdac26f2e41c666335..e2602879d5b096decf18741df21dc184c72df366 100644
--- a/src/lib/parser/shell_expand/words/methods/arrays.rs
+++ b/src/lib/parser/shell_expand/words/methods/arrays.rs
@@ -50,6 +50,18 @@ impl<'a> ArrayMethod<'a> {
             .select(self.selection.clone(), len))
     }
 
+    fn map_keys<'b, E: Expander>(&self, expand_func: &'b E) -> Result<Array, &'static str> {
+        expand_func.map_keys(self.variable, self.selection.clone())
+            .ok_or("no map found")
+            .map(|x| x.cloned().collect())
+    }
+
+    fn map_values<'b, E: Expander>(&self, expand_func: &'b E) -> Result<Array, &'static str> {
+        expand_func.map_values(self.variable, self.selection.clone())
+            .ok_or("no map found")
+            .map(|x| x.collect())
+    }
+
     fn graphemes<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
         let variable = self.resolve_var(expand_func);
         let graphemes: Vec<types::Str> = UnicodeSegmentation::graphemes(variable.as_str(), true)
@@ -174,13 +186,15 @@ impl<'a> ArrayMethod<'a> {
 
     pub(crate) fn handle_as_array<E: Expander>(&self, expand_func: &E) -> Array {
         let res = match self.method {
-            "split" => self.split(expand_func),
-            "split_at" => self.split_at(expand_func),
-            "graphemes" => self.graphemes(expand_func),
             "bytes" => self.bytes(expand_func),
             "chars" => self.chars(expand_func),
+            "graphemes" => self.graphemes(expand_func),
+            "keys" => self.map_keys(expand_func),
             "lines" => self.lines(expand_func),
             "reverse" => self.reverse(expand_func),
+            "split_at" => self.split_at(expand_func),
+            "split" => self.split(expand_func),
+            "values" => self.map_values(expand_func),
             _ => Err("invalid array method"),
         };
 
diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs
index 53868800262e7f2a6374b62bc4c98f8b7957c9e0..8ffe9e37bf5954dd1193dc6bfe86da52e13e9e34 100644
--- a/src/lib/shell/flow.rs
+++ b/src/lib/shell/flow.rs
@@ -1,4 +1,4 @@
-use itertools::{Chunk, Itertools};
+use itertools::Itertools;
 use super::{
     flags::*,
     flow_control::{insert_statement, Case, ElseIf, Function, Statement},
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index 622da12154632f8b830f472090c37815e7226552..326f8ce33107271527da01cdeb7e2c0f930b57c9 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -44,7 +44,7 @@ use self::{
 };
 use builtins::{BuiltinMap, BUILTINS};
 use liner::Context;
-use parser::{pipelines::Pipeline, Expander, Select, Terminator};
+use parser::{pipelines::Pipeline, Expander, MapKeyIter, MapValueIter, Select, Terminator};
 use std::{
     fs::File, io::{self, Read, Write}, iter::FromIterator, ops::Deref, path::Path, process,
     sync::{atomic::Ordering, Arc, Mutex}, time::SystemTime,
@@ -471,7 +471,8 @@ impl<'a> Expander for Shell {
             match selection {
                 Select::All => {
                     let mut array = types::Array::new();
-                    for (_, value) in hmap.iter() {
+                    for (key, value) in hmap.iter() {
+                        array.push(key.clone());
                         let f = format!("{}", value);
                         match *value {
                             VariableType::Str(_) => array.push(f.into()),
@@ -499,7 +500,8 @@ impl<'a> Expander for Shell {
             match selection {
                 Select::All => {
                     let mut array = types::Array::new();
-                    for (_, value) in bmap.iter() {
+                    for (key, value) in bmap.iter() {
+                        array.push(key.clone());
                         let f = format!("{}", value);
                         match *value {
                             VariableType::Str(_) => array.push(f.into()),
@@ -527,6 +529,62 @@ impl<'a> Expander for Shell {
         None
     }
 
+    fn map_keys<'b>(&'b self, name: &str, select: Select) -> Option<MapKeyIter<'b>> {
+        let nvalues;
+        let map: Box<dyn Iterator<Item = &'b types::Str>> =
+            match self.variables.get_ref(name) {
+                Some(&VariableType::HashMap(ref map)) => {
+                    nvalues = map.len();
+                    Box::new(map.keys())
+                }
+                Some(&VariableType::BTreeMap(ref map)) => {
+                    nvalues = map.len();
+                    Box::new(map.keys())
+                }
+                _ => return None
+            };
+
+        match select {
+            Select::All => return Some(map),
+            Select::Range(range) => if let Some((start, length)) = range.bounds(nvalues) {
+                if nvalues > start {
+                    return Some(Box::new(map.skip(start).take(length)));
+                }
+            },
+            _ => ()
+        }
+
+        None
+    }
+
+    fn map_values<'b>(&'b self, name: &str, select: Select) -> Option<MapValueIter<'b>> {
+        let nvalues;
+        let map: Box<dyn Iterator<Item = types::Str>> =
+            match self.variables.get_ref(name) {
+                Some(&VariableType::HashMap(ref map)) => {
+                    nvalues = map.len();
+                    Box::new(map.values().map(|x| format!("{}", x).into()))
+                }
+                Some(&VariableType::BTreeMap(ref map)) => {
+                    nvalues = map.len();
+                    Box::new(map.values().map(|x| format!("{}", x).into()))
+                }
+                _ => return None
+            };
+
+        match select {
+            Select::All => return Some(map),
+            Select::Range(range) => if let Some((start, length)) = range.bounds(nvalues) {
+                if nvalues > start {
+                    return Some(Box::new(map.skip(start).take(length)));
+                }
+            },
+            _ => ()
+        }
+
+        None
+    }
+
     fn tilde(&self, input: &str) -> Option<String> {
         self.variables.tilde_expansion(input, &self.directory_stack)
     }
diff --git a/src/lib/shell/variables/mod.rs b/src/lib/shell/variables/mod.rs
index 89b53f05b5d214f2f1fe3d4913c0327a58a992fa..108a50b1ed257e34797f20b893799afb8c3e54e9 100644
--- a/src/lib/shell/variables/mod.rs
+++ b/src/lib/shell/variables/mod.rs
@@ -315,27 +315,35 @@ impl Variables {
     }
 
     pub fn get_ref(&self, mut name: &str) -> Option<&VariableType> {
-        let mut up_namespace: isize = 0;
-        if name.starts_with("global::") {
-            name = &name["global::".len()..];
+        const GLOBAL_NS: &str = "global::";
+        const SUPER_NS: &str = "super::";
+
+        let mut up_namespace: isize = if name.starts_with(GLOBAL_NS) {
+            name = &name[GLOBAL_NS.len()..];
             // Go up as many namespaces as possible
-            up_namespace = self.scopes().filter(|scope| scope.namespace).count() as isize;
+            self.scopes().filter(|scope| scope.namespace).count() as isize
         } else {
-            while name.starts_with("super::") {
-                name = &name["super::".len()..];
-                up_namespace += 1;
+            let mut up = 0;
+            while name.starts_with(SUPER_NS) {
+                name = &name[SUPER_NS.len()..];
+                up += 1;
             }
-        }
+
+            up
+        };
+
         for scope in self.scopes() {
             match scope.get(name) {
                 val @ Some(VariableType::Function(_)) => return val,
                 val @ Some(_) if up_namespace == 0 => return val,
                 _ => (),
             }
+
             if scope.namespace {
                 up_namespace -= 1;
             }
         }
+
         None
     }