DEV Community

Cover image for Default map value
Nicolas Fränkel
Nicolas Fränkel

Posted on • Originally published at blog.frankel.ch

4 1 1 1 1

Default map value

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
}
Enter fullscreen mode Exit fullscreen mode
  1. Initialize an empty map
  2. Attempt to retrieve a non-existent key
  3. 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
Enter fullscreen mode Exit fullscreen mode
  1. Retrieve the value with a default in one step

Kotlin

Kotlin provides several approaches to retrieve 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
Enter fullscreen mode Exit fullscreen mode
  1. Retrieve the default value
  2. 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
Enter fullscreen mode Exit fullscreen mode
  1. Raises a KeyError

To avoid this, Python offers the get() method:

map = {}
value = map.get('absent', 'default')                     #1
Enter fullscreen mode Exit fullscreen mode

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']
Enter fullscreen mode Exit fullscreen mode
  1. Automatically provide a default value for any absent key

Ruby

Ruby's default behavior returns nil for absent keys:

map = {}
value = map['absent']
Enter fullscreen mode Exit fullscreen mode

For a default value, use the fetch method:

map = {}
value = map.fetch('absent', 'default')                  #1
Enter fullscreen mode Exit fullscreen mode
  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
Enter fullscreen mode Exit fullscreen mode
  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
Enter fullscreen mode Exit fullscreen mode
  1. Initialize a new map
  2. Iterate over key-value pairs
  3. 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
Enter fullscreen mode Exit fullscreen mode
  1. Initialize a new map
  2. Loop over the pairs of key values
  3. 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
Enter fullscreen mode Exit fullscreen mode

The result is the following:

1   one
a   A
b   B
Enter fullscreen mode Exit fullscreen mode

In Lua, absent keys return nil by default:

map = {}
value = map['absent']
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  1. Create the table
  2. Create a metatable
  3. Associate the metatable with the table
  4. Define the __index function to return the absent key
  5. 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 Value
Programming language Per call Per map Static Lazy
Java
Kotlin
Python
Ruby
Lua

Originally published at A Java Geek on August 11th, 2024

Reinvent your career. Join DEV.

It takes one minute and is worth it for your career.

Get started

Top comments (5)

Collapse
 
programcrafter profile image
ProgramCrafter

For Lua, you can use essentially same trick as in older Java:

local map = {}
local value = map['absent']

value = value or 'default'
-- if `false` or other false-evaluated values must be kept, then rather
--     if value == nil then value = 'default' end
Enter fullscreen mode Exit fullscreen mode
Collapse
 
fyodorio profile image
Fyodor

You must be kidding: language comparison in 2024 without TS and Rust?! 😡😄

Collapse
 
nfrankel profile image
Nicolas Fränkel

I can't do everything 😅

Collapse
 
fyodorio profile image
Fyodor

Use AI as all them youngsters do 😄

Thread Thread
 
nfrankel profile image
Nicolas Fränkel

If I do, then I learn nothing

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay