Book Image

D Cookbook

By : Adam Ruppe
Book Image

D Cookbook

By: Adam Ruppe

Overview of this book

Table of Contents (21 chapters)
D Cookbook
Credits
Foreword
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

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:

  1. Declare the associative array with string keys and string values.

  2. Initialize it with initial data.

  3. 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.

  4. 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.