add at function
diff --git a/README.md b/README.md
index b3a2392..4a1c0ec 100644
--- a/README.md
+++ b/README.md
@@ -181,6 +181,7 @@
 
 // Range function, useful for loops
 render("{% for i in range(4) %}{{ loop.index1 }}{% endfor %}", data); // "1234"
+render("{% for i in range(3) %}{{ at(guests, i) }} {% endfor %}", data); // "Jeff Tom Patrick "
 
 // Length function (please don't combine with range, use list directly...)
 render("I count {{ length(guests) }} guests.", data); // "I count 3 guests."
diff --git a/include/inja/bytecode.hpp b/include/inja/bytecode.hpp
index f473691..8a3bce3 100644
--- a/include/inja/bytecode.hpp
+++ b/include/inja/bytecode.hpp
@@ -38,6 +38,7 @@
     GreaterEqual,
     Less,
     LessEqual,
+    At,
     Different,
     DivisibleBy,
     Even,
diff --git a/include/inja/parser.hpp b/include/inja/parser.hpp
index ca63b16..c2364ba 100644
--- a/include/inja/parser.hpp
+++ b/include/inja/parser.hpp
@@ -18,6 +18,7 @@
 
 class ParserStatic {
   ParserStatic() {
+    functions.add_builtin("at", 2, Bytecode::Op::At);
     functions.add_builtin("default", 2, Bytecode::Op::Default);
     functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy);
     functions.add_builtin("even", 1, Bytecode::Op::Even);
@@ -183,8 +184,8 @@
               append_callback(tmpl, func_token.text, num_args);
               return true;
             }
-          } else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") || 
-              m_tok.text == static_cast<decltype(m_tok.text)>("false") || 
+          } else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") ||
+              m_tok.text == static_cast<decltype(m_tok.text)>("false") ||
               m_tok.text == static_cast<decltype(m_tok.text)>("null")) {
             // true, false, null are json literals
             if (brace_level == 0 && bracket_level == 0) {
diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp
index a877bf9..2266c9a 100644
--- a/include/inja/renderer.hpp
+++ b/include/inja/renderer.hpp
@@ -132,8 +132,8 @@
     enum class Type { Map, Array };
 
     Type loop_type;
-    nonstd::string_view key_name;       // variable name for keys
-    nonstd::string_view value_name;     // variable name for values
+    nonstd::string_view key_name;   // variable name for keys
+    nonstd::string_view value_name; // variable name for values
     json data;                      // data with loop info added
 
     json values;                    // values to iterate over
@@ -145,8 +145,8 @@
     // loop over map
     using KeyValue = std::pair<nonstd::string_view, json*>;
     using MapValues = std::vector<KeyValue>;
-    MapValues map_values;            // values to iterate over
-    MapValues::iterator map_it;      // iterator over values
+    MapValues map_values;           // values to iterate over
+    MapValues::iterator map_it;     // iterator over values
 
   };
 
@@ -235,6 +235,13 @@
           m_stack.emplace_back(std::move(result));
           break;
         }
+        case Bytecode::Op::At: {
+          auto args = get_args(bc);
+          auto result = args[0]->at(args[1]->get<int>());
+          pop_args(bc);
+          m_stack.emplace_back(result);
+          break;
+        }
         case Bytecode::Op::First: {
           auto result = get_args(bc)[0]->front();
           pop_args(bc);
diff --git a/single_include/inja/inja.hpp b/single_include/inja/inja.hpp
index fa327b8..679b420 100644
--- a/single_include/inja/inja.hpp
+++ b/single_include/inja/inja.hpp
@@ -1441,6 +1441,7 @@
     GreaterEqual,
     Less,
     LessEqual,
+    At,
     Different,
     DivisibleBy,
     Even,
@@ -2017,6 +2018,7 @@
 
 class ParserStatic {
   ParserStatic() {
+    functions.add_builtin("at", 2, Bytecode::Op::At);
     functions.add_builtin("default", 2, Bytecode::Op::Default);
     functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy);
     functions.add_builtin("even", 1, Bytecode::Op::Even);
@@ -2182,8 +2184,8 @@
               append_callback(tmpl, func_token.text, num_args);
               return true;
             }
-          } else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") || 
-              m_tok.text == static_cast<decltype(m_tok.text)>("false") || 
+          } else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") ||
+              m_tok.text == static_cast<decltype(m_tok.text)>("false") ||
               m_tok.text == static_cast<decltype(m_tok.text)>("null")) {
             // true, false, null are json literals
             if (brace_level == 0 && bracket_level == 0) {
@@ -2738,8 +2740,8 @@
     enum class Type { Map, Array };
 
     Type loop_type;
-    nonstd::string_view key_name;       // variable name for keys
-    nonstd::string_view value_name;     // variable name for values
+    nonstd::string_view key_name;   // variable name for keys
+    nonstd::string_view value_name; // variable name for values
     json data;                      // data with loop info added
 
     json values;                    // values to iterate over
@@ -2751,8 +2753,8 @@
     // loop over map
     using KeyValue = std::pair<nonstd::string_view, json*>;
     using MapValues = std::vector<KeyValue>;
-    MapValues map_values;            // values to iterate over
-    MapValues::iterator map_it;      // iterator over values
+    MapValues map_values;           // values to iterate over
+    MapValues::iterator map_it;     // iterator over values
 
   };
 
@@ -2841,6 +2843,13 @@
           m_stack.emplace_back(std::move(result));
           break;
         }
+        case Bytecode::Op::At: {
+          auto args = get_args(bc);
+          auto result = args[0]->at(args[1]->get<int>());
+          pop_args(bc);
+          m_stack.emplace_back(result);
+          break;
+        }
         case Bytecode::Op::First: {
           auto result = get_args(bc)[0]->front();
           pop_args(bc);
diff --git a/test/unit-renderer.cpp b/test/unit-renderer.cpp
index bcd56dd..6380948 100644
--- a/test/unit-renderer.cpp
+++ b/test/unit-renderer.cpp
@@ -62,11 +62,10 @@
 		CHECK( env.render("{% for name in names %}{{ loop.index }}: {{ name }}{% if not loop.is_last %}, {% endif %}{% endfor %}!", data) == "0: Jeff, 1: Seb!" );
 		CHECK( env.render("{% for name in names %}{{ loop.index }}: {{ name }}{% if loop.is_last == false %}, {% endif %}{% endfor %}!", data) == "0: Jeff, 1: Seb!" );
 
-		data["empty_loop"] = {};
-		CHECK( env.render("{% for name in empty_loop %}a{% endfor %}", data) == "" );
 		CHECK( env.render("{% for name in {} %}a{% endfor %}", data) == "" );
 
 		CHECK_THROWS_WITH( env.render("{% for name ins names %}a{% endfor %}", data), "[inja.exception.parser_error] expected 'in', got 'ins'" );
+		CHECK_THROWS_WITH( env.render("{% for name in empty_loop %}a{% endfor %}", data), "[inja.exception.render_error] variable 'empty_loop' not found" );
 		// CHECK_THROWS_WITH( env.render("{% for name in relatives %}{{ name }}{% endfor %}", data), "[inja.exception.json_error] [json.exception.type_error.302] type must be array, but is object" );
 	}
 
@@ -144,6 +143,7 @@
 	data["brother"]["daughters"] = {"Maria", "Helen"};
 	data["property"] = "name";
 	data["age"] = 29;
+	data["i"] = 1;
 	data["is_happy"] = true;
 	data["is_sad"] = false;
 	data["vars"] = {2, 3, 4, 0, -1, -2, -3};
@@ -182,6 +182,11 @@
 		// CHECK_THROWS_WITH( env.render("{{ sort(5) }}", data), "[inja.exception.json_error] [json.exception.type_error.302] type must be array, but is number" );
 	}
 
+	SECTION("at") {
+		CHECK( env.render("{{ at(names, 0) }}", data) == "Jeff" );
+		CHECK( env.render("{{ at(names, i) }}", data) == "Seb" );
+	}
+
 	SECTION("first") {
 		CHECK( env.render("{{ first(names) }}", data) == "Jeff" );
 		// CHECK_THROWS_WITH( env.render("{{ first(5) }}", data), "[inja.exception.json_error] [json.exception.type_error.302] type must be array, but is number" );