Using associative arrays to translate input
D also has associative arrays, sometimes called maps or dictionaries. An associative array maps arbitrary keys to values. Unlike a regular array, the keys do not need to be sequential and do not need to be integers. Here, you'll explore their functionality by creating a program that translates input strings to other strings.
How to do it…
Let's translate an input by using the following steps:
Declare the associative array with string keys and string values.
Initialize it with initial data.
Loop over input lines. If the line is in the array, show the value and remove it. If not, add this line with a replacement.
When you're done, loop over the array to show your changes.
The code is as follows:
void main() { import std.stdio, std.string; string[string] replacements = ["test" : "passed", "text" : "replaced"]; replacements["foo"] = "bar"; assert(replacements["test"] == "passed"); foreach(line; stdin.byLine()) { line = line.strip(); // cut off whitespace // see if the given line is in the mapping… if(auto replacement = line in replacements) { // if yes, show the replacement, then unmap it writeln(line, " => ", *replacement); replacements.remove(line.idup); } else { // if no, add it to the map writeln(line); replacements[line.idup] = "previously inserted!"; } } foreach(line, replacement; replacements) writeln("Mapping ", line, " => ", replacement);); }
When the program runs out of lines to process, it will print out the current array contents, showing you what has been added and removed as you entered data.
How it works…
First, you declared your main
function and then imported the std.stdio
and std.string
modules, which contain the I/O and whitespace stripping functions that you used later.
Next, you declared an associative array that maps strings to other strings. The syntax is ValueType[KeyType]
, and both sides can be of any D type. You also initialized the replacements with an associative array literal.
Note
It is also possible to use user-defined types as associative array keys. Using custom types as key types requires that they implement opHash
, opCmp
, and opEquals
.
The syntax of an associative array (AA) literal is [Key:Value, Key:Value, …]
. AA literals can have both compile-time constant and runtime data; ["foo":x]
is legal too.
Next, you can set a value outside the literal and check the value of a key, just for demonstration purposes. Associative arrays have similar syntax to regular arrays: you use the same bracket syntax to get and set elements.
Then, you can enter the replacement loop, reading the standard input by line and then stripping off whitespace and looking for the line in the replacements array. Let's look at this line in more detail, as follows:
if(auto replacement = line in replacements) {
On the right-hand side, you can use the in
operator to do a key lookup. This operator returns a pointer to the element if it is found, and null if it is not found.
Tip
You don't have to use the pointer returned by the in
operator. if(line in replacements)
works just as well. There's also the inverse of in
, which is !in
. The if(line !in replacements)
statement is true if line is not in the replacements array.
On the left-hand side, you can declare and assign the variable right inside the if
statement. This keeps the newly declared variable limited in scope. If the variable replacement is available, you can be certain that it is not null, since the if
statement will not execute otherwise!
In the next example, you'll proceed into the true
branch of the if
statement. This branch uses the dereference operator, *replacement
, to print out the value. The *
operator is necessary because the in
operator returns a pointer to the element rather than the element itself. Then you'll remove this key from the mapping by using the built-in associative array property remove
. Next time you insert that line, it will not be replaced.
After that, the false branch of the if
statement does not have the null
pointer stored in the variable replacement
available to use. Any attempt to access it will be a compile error. Instead, you can add the new line to the replacement map. The .idup
property is required because associative array keys must be immutable, and stdin.byLine
returns a mutable buffer. Array.idup
creates a new, immutable copy of the data.
Finally, once the input has been exhausted, you can loop over the associative array with a foreach
loop. The syntax is foreach(index, value; array)
, and you can print out the current state. The index
parameter is optional if you only need the values.
There's more…
You can also get a list of all keys and values with the .keys
and .values
properties on the associative array. The std.traits.KeyType
and std.traits.ValueType
variables can be used to do compile-time reflection of generic AA types.