Lists let you store lots of variables, and to access them by their location in the list. However, there are times when you want to store lots of variables, but access them using more complex relationships. One example is a dictionary, which lets you store variables and access them using a key.

Dictionaries in R are created by associating names (keys) with elements in a list. You thus create them using the same combine (c) function as used to create a list. Make a new file called dict.R and put this in it:

sounds <- c("cat"="meow", "dog"="woof", "horse"="neigh")

cat_sound <- sounds["cat"]

print(cat_sound)

Running that script, you should see

cat
"meow" 

printed to the screen.

What we did here was create a dictionary by adding names (keys) to the items in the list. The three items in the list are meow, woof and neigh. We have added names (keys) using the syntax name=value, so meow is given the name cat, woof is named dog and neigh is named horse.

The value is the real data that we want to keep hold of and the name (also called key) is how we can get at the data we want. The key and value are separated by an equals sign, and each key-value pair is separated by a comma.

On the next line we access the data in the dictionary sounds. Again, like lists we use the square brackets to ask questions of our data. In this case we’re asking the dictionary to give us the value associated with the key cat and so it will return to us meow.

Since dictionaries can be quite large and it can sometimes be hard to see which parts are keys and which are values, it is possible to write dictionaries over multiple lines, one line per key-value item:

sounds <- c(
"cat"="meow",
"dog"="woof",
"horse"="neigh"
)

cat_sound <- sounds["cat"]

print(cat_sound)

Note that an R dictionary is just a list with named elements. This means you can still access the items by index, e.g. sounds[1] will return cat. This also means that you don’t need to name every element!

Also note that

cat
"meow"

was printed to the screen as the value with its name (key) is returned. To get the value alone, you need to use as.character if the value is a string, or as.numeric if the value is a number, e.g.

sounds <- c(
"cat"="meow",
"dog"="woof",
"horse"="neigh"
)

cat_sound <- sounds["cat"]

print(as.character(cat_sound))

would print

[1] "meow"

EXERCISE

Edit dict.R to ask for the sound for the dog and the horse.

What happens if you ask for an animal that isn’t in the dictionary?

Adding new data into dictionaries

As with lists, dictionaries are dynamic so we can add entries into a dictionary. Let’s say that we want to add in a new sound for a cow into our sounds dictionary. The key that the data will have will be “cow” and the value will be “moo”. To do so we put sounds["cow"] on the left-hand side of a variable assignment expression, as if we’re making a new variable. On the right goes the data that we want to put into the dictionary:

sounds <- c(
"cat"="meow",
"dog"="woof",
"horse"="neigh"
)

sounds["cow"] <- "moo"

print(sounds)

This is saying that we want the append the value moo onto the end of sounds, and to then associated it with the name (key) cow. This should print;

    cat     dog   horse     cow
"meow"  "woof" "neigh"   "moo" 

EXERCISE

Edit dict.R so that the dictionary is initially defined with only the cat and dog entries. Add the entry for the horse and then the cow dynamically

Looping over dictionaries

When discussing for loops you were told that R allows you to loop over lots of different types of data such as lists and sequences. Since a dictionary is just a list (vector) with names, we can loop over dictionaries too.

To discover how it works, let’s do the naïve thing first and just see what happens when we loop over a dictionary:

sounds <- c(
"cat"="meow",
"dog"="woof",
"horse"="neigh"
)

for (thing in sounds){
print(thing)
}

If you put this code into a file called dict_loop.R and run it you should see:

[1] "meow"
[1] "woof"
[1] "neigh"

Hopefully, you recognise those as the values from the dictionary. So, it seems that when looping over a dictionary we will be given the values. This makes sense, as a dictionary is just a list with named values.

What if, for example, you wanted to loop over the names (keys) instead. Well, there is a function called names which gives you just those so that you can loop over them:

sounds <- c(
"cat"="meow",
"dog"="woof",
"horse"="neigh"
)

for (thing in names(sounds)){
print(thing)
}

will print:

[1] "cat"
[1] "dog"
[1] "horse"

If we want to loop over the dictionary and get both the keys and the values, we can remember that a dictionary is just a list. Thus we can get both the name and value by looking up the item by its numeric index:

sounds <- c(
"cat"="meow",
"dog"="woof",
"horse"="neigh"
)

for (i in seq(1,length(sounds))){
sound <- sounds[i]
print(paste(names(sound), "goes", sound))
}

The length function gives the number of items in the dictionary, which is passed to seq to generate a sequence of all of the numeric indicies. For each index we look up the sound as sounds[i]. This contains both the value (e.g. meow) and an associated name (e.g. cat). The name is extracted using the names function.

EXERCISE

Make a dictionary with the keys being the name of countries and the value being the country’s capital city. Loop over the dictionary and print something like The capital of France is Paris for each item.