In this post, I'll explain how to provide a default value when querying an absent key in a hash map in different programming languages. Java Let's start with Java, my first professional programming language. In older versions, retrieving a value from a map required using the get() method: Map map = new HashMap(); //1 Object value = map.get(new Object()); //2 if (value == null) { value = "default"; //3 } Initialize an empty map Attempt to retrieve a non-existent key Assign a default value if the key is absent With Java 1.8, the Map interface introduced a more concise way to handle absent keys: var map = new HashMap<Object, String>(); var value = map.getOrDefault(new Object(), "default"); //1 Retrieve the value with a default in one step Kotlin Kotlin provides several approaches to retrieving values from a map: get() and getOrDefault() function just like their Java counterparts. getValue() throws an exception if the key is missing. getOrElse() accepts a lambda to provide a default value lazily. val map = mapOf<Any, String>() val default = map.getOrDefault("absent", "default") //1 val lazyDefault = map.getOrElse("absent") { "default" } //2 Retrieve the default value Lazily evaluate the default value Python Python is less forgiving than Java when handling absent keys—it raises a KeyError: map = {} value = map['absent'] #1 Raises a KeyError To avoid this, Python offers the get() method: map = {} value = map.get('absent', 'default') #1 Alternatively, Python's collections.defaultdict allows setting a default for all absent keys: from collections import defaultdict map = defaultdict(lambda: 'default') #1 value = map['absent'] Automatically provide a default value for any absent key Ruby Ruby's default behavior returns nil for absent keys: map = {} value = map['absent'] For a default value, use the fetch method: map = {} value = map.fetch('absent', 'default') #1 Provide a default value for the absent key Ruby also supports a more flexible approach with closures: map = {} value = map.fetch('absent') { |key| key } #1 Return the queried key instead of a constant Lua My experience with Lua is relatively new, having picked it up for Apache APISIX. Let's start with Lua's map syntax: map = {} --1 map["a"] = "A" map["b"] = "B" map["c"] = "C" for k, v in pairs(map) do --2 print(k, v) --3 end Initialize a new map Iterate over key-value pairs Print each key-value pair Fun fact: the syntax for tables is the same as for maps: table = {} --1 table[0] = "zero" table[1] = "one" table[2] = "two" for k,v in ipairs(table) do --2 print(k, v) --3 end Initialize a new map Loop over the pairs of key values Print the following: 1 one 2 two Lua arrays start at index 0! We can mix and match indices and keys. The syntax is similar, but there's no difference between a table and a map. Indeed, Lua calls the data structure a table: something = {} something["a"] = "A" something[1] = "one" something["b"] = "B" for k,v in pairs(something) do print(k, v) end The result is the following: 1 one a A b B I n Lua, absent keys return nil by default: map = {} value = map['absent'] To provide a default, Lua uses metatables and the __index metamethod: Metatables allow us to change the behavior of a table. For instance, using metatables, we can define how Lua computes the expression a+b, where a and b are tables. Whenever Lua tries to add two tables, it checks whether either of them has a metatable and whether that metatable has an __add field. If Lua finds this field, it calls the corresponding value (the so-called metamethod, which should be a function) to compute the sum. -- Metatables and Metamethods Each table in Lua may have its own metatable. As I said earlier, when we access an absent field in a table, the result is nil. This is true, but it is not the whole truth. Such access triggers the interpreter to look for an __index metamethod: if there is no such method, as usually happens, then the access results in nil; otherwise, the metamethod will provide the result. -- The __index Metamethod Here's how to use it: table = {} --1 mt = {} --2 setmetatable(table, mt) --3 mt.__index = function (table, key) --4 return key end default = table['absent'] --5 Create the table Create a metatable Associate the metatable with the table Define the __index function to return the absent key The __index function is called because the key is absent Summary This post explored how to provide default values when querying absent keys across various programming languages. Here's a quick summary: Scope Scope Value Value Programming Language Per call Per map Static Lazy Java ❎ ❌ ❎ ❌ Kotlin ❎ ❌ ❎ ❎ Python ❎ ❎ ❌ ❎ Ruby ❎ ❌ ❎ ❎ Lua ❌ ❎ ❎ ❌ In this post, I'll explain how to provide a default value when querying an absent key in a hash map in different programming languages. Java Let's start with Java, my first professional programming language. In older versions, retrieving a value from a map required using the get() method: get() Map map = new HashMap(); //1 Object value = map.get(new Object()); //2 if (value == null) { value = "default"; //3 } Map map = new HashMap(); //1 Object value = map.get(new Object()); //2 if (value == null) { value = "default"; //3 } Initialize an empty map Attempt to retrieve a non-existent key Assign a default value if the key is absent Initialize an empty map Attempt to retrieve a non-existent key Assign a default value if the key is absent With Java 1.8, the Map interface introduced a more concise way to handle absent keys: Map var map = new HashMap<Object, String>(); var value = map.getOrDefault(new Object(), "default"); //1 var map = new HashMap<Object, String>(); var value = map.getOrDefault(new Object(), "default"); //1 Retrieve the value with a default in one step Retrieve the value with a default in one step Kotlin Kotlin provides several approaches to retrieving values from a map: get() and getOrDefault() function just like their Java counterparts. getValue() throws an exception if the key is missing. getOrElse() accepts a lambda to provide a default value lazily. get() and getOrDefault() function just like their Java counterparts. get() getOrDefault() getValue() throws an exception if the key is missing. getValue() getOrElse() accepts a lambda to provide a default value lazily. getOrElse() val map = mapOf<Any, String>() val default = map.getOrDefault("absent", "default") //1 val lazyDefault = map.getOrElse("absent") { "default" } //2 val map = mapOf<Any, String>() val default = map.getOrDefault("absent", "default") //1 val lazyDefault = map.getOrElse("absent") { "default" } //2 Retrieve the default value Lazily evaluate the default value Retrieve the default value Lazily evaluate the default value Python Python is less forgiving than Java when handling absent keys—it raises a KeyError : KeyError map = {} value = map['absent'] #1 map = {} value = map['absent'] #1 Raises a KeyError Raises a KeyError KeyError To avoid this, Python offers the get() method: get() map = {} value = map.get('absent', 'default') #1 map = {} value = map.get('absent', 'default') #1 Alternatively, Python's collections.defaultdict allows setting a default for all absent keys: collections.defaultdict from collections import defaultdict map = defaultdict(lambda: 'default') #1 value = map['absent'] from collections import defaultdict map = defaultdict(lambda: 'default') #1 value = map['absent'] Automatically provide a default value for any absent key Automatically provide a default value for any absent key Ruby Ruby's default behavior returns nil for absent keys: nil map = {} value = map['absent'] map = {} value = map['absent'] For a default value, use the fetch method: fetch map = {} value = map.fetch('absent', 'default') #1 map = {} value = map.fetch('absent', 'default') #1 Provide a default value for the absent key Provide a default value for the absent key Ruby also supports a more flexible approach with closures: map = {} value = map.fetch('absent') { |key| key } #1 map = {} value = map.fetch('absent') { |key| key } #1 Return the queried key instead of a constant Return the queried key instead of a constant Lua My experience with Lua is relatively new, having picked it up for Apache APISIX. Let's start with Lua's map syntax: map = {} --1 map["a"] = "A" map["b"] = "B" map["c"] = "C" for k, v in pairs(map) do --2 print(k, v) --3 end map = {} --1 map["a"] = "A" map["b"] = "B" map["c"] = "C" for k, v in pairs(map) do --2 print(k, v) --3 end Initialize a new map Iterate over key-value pairs Print each key-value pair Initialize a new map Iterate over key-value pairs Print each key-value pair Fun fact: the syntax for tables is the same as for maps: table = {} --1 table[0] = "zero" table[1] = "one" table[2] = "two" for k,v in ipairs(table) do --2 print(k, v) --3 end table = {} --1 table[0] = "zero" table[1] = "one" table[2] = "two" for k,v in ipairs(table) do --2 print(k, v) --3 end Initialize a new map Loop over the pairs of key values Print the following: 1 one 2 two Lua arrays start at index 0! Initialize a new map Initialize a new map Loop over the pairs of key values Loop over the pairs of key values Print the following: 1 one 2 two Lua arrays start at index 0! Print the following: 1 one 2 two 1 one 2 two Lua arrays start at index 0! We can mix and match indices and keys. The syntax is similar, but there's no difference between a table and a map. Indeed, Lua calls the data structure a table: something = {} something["a"] = "A" something[1] = "one" something["b"] = "B" for k,v in pairs(something) do print(k, v) end something = {} something["a"] = "A" something[1] = "one" something["b"] = "B" for k,v in pairs(something) do print(k, v) end The result is the following: 1 one a A b B 1 one a A b B I n Lua, absent keys return nil by default: nil map = {} value = map['absent'] map = {} value = map['absent'] To provide a default, Lua uses metatables and the __index metamethod: __index Metatables allow us to change the behavior of a table. For instance, using metatables, we can define how Lua computes the expression a+b, where a and b are tables. Whenever Lua tries to add two tables, it checks whether either of them has a metatable and whether that metatable has an __add field. If Lua finds this field, it calls the corresponding value (the so-called metamethod, which should be a function) to compute the sum. -- Metatables and Metamethods Metatables allow us to change the behavior of a table. For instance, using metatables, we can define how Lua computes the expression a+b , where a and b are tables. Whenever Lua tries to add two tables, it checks whether either of them has a metatable and whether that metatable has an __add field. If Lua finds this field, it calls the corresponding value (the so-called metamethod , which should be a function) to compute the sum. a+b a b __add metamethod -- Metatables and Metamethods Metatables and Metamethods Each table in Lua may have its own metatable . metatable As I said earlier, when we access an absent field in a table, the result is nil. This is true, but it is not the whole truth. Such access triggers the interpreter to look for an __index metamethod: if there is no such method, as usually happens, then the access results in nil; otherwise, the metamethod will provide the result. -- The __index Metamethod As I said earlier, when we access an absent field in a table, the result is nil . This is true, but it is not the whole truth. Such access triggers the interpreter to look for an __index metamethod: if there is no such method, as usually happens, then the access results in nil ; otherwise, the metamethod will provide the result. nil __index nil -- The __index Metamethod The __index Here's how to use it: table = {} --1 mt = {} --2 setmetatable(table, mt) --3 mt.__index = function (table, key) --4 return key end default = table['absent'] --5 table = {} --1 mt = {} --2 setmetatable(table, mt) --3 mt.__index = function (table, key) --4 return key end default = table['absent'] --5 Create the table Create a metatable Associate the metatable with the table Define the __index function to return the absent key The __index function is called because the key is absent Create the table Create a metatable Associate the metatable with the table Define the __index function to return the absent key __index The __index function is called because the key is absent __index Summary This post explored how to provide default values when querying absent keys across various programming languages. Here's a quick summary: Scope Scope Value Value Programming Language Per call Per map Static Lazy Java ❎ ❌ ❎ ❌ Kotlin ❎ ❌ ❎ ❎ Python ❎ ❎ ❌ ❎ Ruby ❎ ❌ ❎ ❎ Lua ❌ ❎ ❎ ❌ Scope Scope Value Value Programming Language Per call Per map Static Lazy Java ❎ ❌ ❎ ❌ Kotlin ❎ ❌ ❎ ❎ Python ❎ ❎ ❌ ❎ Ruby ❎ ❌ ❎ ❎ Lua ❌ ❎ ❎ ❌ Scope Scope Value Value Scope Scope Scope Scope Value Value Value Value Programming Language Per call Per map Static Lazy Programming Language Programming Language Per call Per call Per map Per map Static Static Lazy Lazy Java ❎ ❌ ❎ ❌ Java Java ❎ ❎ ❌ ❌ ❎ ❎ ❌ ❌ Kotlin ❎ ❌ ❎ ❎ Kotlin Kotlin ❎ ❎ ❌ ❌ ❎ ❎ ❎ ❎ Python ❎ ❎ ❌ ❎ Python Python ❎ ❎ ❎ ❎ ❌ ❌ ❎ ❎ Ruby ❎ ❌ ❎ ❎ Ruby Ruby ❎ ❎ ❌ ❌ ❎ ❎ ❎ ❎ Lua ❌ ❎ ❎ ❌ Lua Lua ❌ ❌ ❎ ❎ ❎ ❎ ❌ ❌