Ddoc
$(SPEC_S Associative Arrays,
$(P Associative arrays have an index that is not necessarily an integer,
and can be sparsely populated. The index for an associative array
is called the $(I key), and its type is called the $(I KeyType).
)
$(P Associative arrays are declared by placing the $(I KeyType)
within the [] of an array declaration:
)
---------
int[char[]] b; // associative array b of ints that are
// indexed by an array of characters.
// The $(I KeyType) is char[]
b["hello"] = 3; // set value associated with key "hello" to 3
func(b["hello"]); // pass 3 as parameter to func()
---------
$(P Particular keys in an associative array can be removed with the
remove function:
)
---------
b.$(B remove)("hello");
---------
$(P The $(I InExpression) yields a pointer to the value
if the key is in the associative array, or $(B null) if not:
)
---------
int* p;
p = ("hello" $(B in) b);
if (p != $(B null))
...
---------
$(P $(I KeyType)s cannot be functions or voids.
)
$(P The element types of an associative array cannot be functions or voids.)
Using Classes as the KeyType
$(P Classes can be used as the $(I KeyType). For this to work,
the class definition must override the following member functions
of class $(TT Object):)
$(UL
$(LI $(TT hash_t toHash()))
$(V1 $(LI $(TT int opEquals(Object))))
$(V2 $(LI $(TT bool opEquals(Object))))
$(LI $(TT int opCmp(Object)))
)
$(P $(TT hash_t) is an alias to an integral type.)
$(P Note that the parameter to $(TT opCmp) and $(TT opEquals) is
of type
$(TT Object), not the type of the class in which it is defined.)
$(P For example:)
---
class Foo
{
int a, b;
hash_t $(B toHash)() { return a + b; }
$(V1 int $(B opEquals)(Object o))$(V2 bool $(B opEquals)(Object o))
{ Foo f = cast(Foo) o;
return f && a == foo.a && b == foo.b;
}
int $(B opCmp)(Object o)
{ Foo f = cast(Foo) o;
if (!f)
return -1;
if (a == foo.a)
return b - foo.b;
return a - foo.a;
}
}
---
$(P The implementation may use either $(TT opEquals) or $(TT opCmp) or
both. Care should be taken so that the results of
$(TT opEquals) and $(TT opCmp) are consistent with each other when
the class objects are the same or not.)
Using Structs or Unions as the KeyType
$(P If the $(I KeyType) is a struct or union type,
a default mechanism is used
to compute the hash and comparisons of it based on the binary
data within the struct value. A custom mechanism can be used
by providing the following functions as struct members:
)
---------
$(V2 const) hash_t $(B toHash)();
$(V1 int $(B opEquals)($(I KeyType)* s);)$(V2 const bool $(B opEquals)(ref const $(I KeyType) s);)
$(V1 int $(B opCmp)($(I KeyType)* s);)$(V2 const int $(B opCmp)(ref const $(I KeyType) s);)
---------
$(P For example:)
---------
import std.string;
struct MyString
{
string str;
$(V1 hash_t $(B toHash)()
{ hash_t hash;
foreach (char c; s)
hash = (hash * 9) + c;
return hash;
}
bool $(B opEquals)(MyString* s)
{
return std.string.cmp(this.str, s.str) == 0;
}
int $(B opCmp)(MyString* s)
{
return std.string.cmp(this.str, s.str);
})
$(V2 const hash_t $(B toHash)()
{ hash_t hash;
foreach (char c; s)
hash = (hash * 9) + c;
return hash;
}
const bool $(B opEquals)(ref const MyString s)
{
return std.string.cmp(this.str, s.str) == 0;
}
const int $(B opCmp)(ref const MyString s)
{
return std.string.cmp(this.str, s.str);
})
}
---------
$(P The implementation may use either $(TT opEquals) or $(TT opCmp) or
both. Care should be taken so that the results of
$(TT opEquals) and $(TT opCmp) are consistent with each other when
the struct/union objects are the same or not.)
Properties
Properties for associative arrays are:
$(TABLE2 Associative Array Properties,
$(TR $(TH Property) $(TH Description))
$(TR
$(TD $(B .sizeof))
$(TD Returns the size of the reference to the associative
array; it is typically 8.
)
)
$(TR
$(TD $(B .length))
$(TD Returns number of values in the associative array.
Unlike for dynamic arrays, it is read-only.
)
)
$(TR
$(TD $(B .keys))
$(TD Returns dynamic array, the elements of which are the keys in
the associative array.
)
)
$(TR
$(TD $(B .values))
$(TD Returns dynamic array, the elements of which are the values in
the associative array.
)
)
$(TR
$(TD $(B .rehash))
$(TD Reorganizes the associative array in place so that lookups
are more efficient. rehash is effective when, for example,
the program is done loading up a symbol table and now needs
fast lookups in it.
Returns a reference to the reorganized array.
)
)
$(V2
$(TR
$(TD $(B .byKey()))
$(TD Returns a delegate suitable for use as an $(I Aggregate) to
a $(LINK2 statement.html#ForeachStatement, $(I ForeachStatement))
which will iterate over the keys
of the associative array.
)
)
$(TR
$(TD $(B .byValue()))
$(TD Returns a delegate suitable for use as an $(I Aggregate) to
a $(LINK2 statement.html#ForeachStatement, $(I ForeachStatement))
which will iterate over the values
of the associative array.
)
)
$(TR
$(TD $(B .get(Key key, lazy Value defaultValue)))
$(TD Looks up $(I key); if it exists returns corresponding $(I value)
else evaluates and returns $(I defaultValue).
)
)
)
)
Associative Array Example: word count
---------
import std.file; // D file I/O
import std.stdio;
int main (string[] args)
{
int word_total;
int line_total;
int char_total;
int[char[]] dictionary;
writefln(" lines words bytes file");
for (int i = 1; i < args.length; ++i) // program arguments
{
char[] input; // input buffer
int w_cnt, l_cnt, c_cnt; // word, line, char counts
int inword;
int wstart;
// read file into input[]
input = cast(char[])std.file.read(args[i]);
foreach (j, char c; input)
{
if (c == '\n')
++l_cnt;
if (c >= '0' && c <= '9')
{
}
else if (c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z')
{
if (!inword)
{
wstart = j;
inword = 1;
++w_cnt;
}
}
else if (inword)
{
char[] word = input[wstart .. j];
dictionary[word]++; // increment count for word
inword = 0;
}
++c_cnt;
}
if (inword)
{
char[] word = input[wstart .. input.length];
dictionary[word]++;
}
writefln("%8d%8d%8d %s", l_cnt, w_cnt, c_cnt, args[i]);
line_total += l_cnt;
word_total += w_cnt;
char_total += c_cnt;
}
if (args.length > 2)
{
writef("-------------------------------------\n%8d%8d%8d total",
line_total, word_total, char_total);
}
writefln("-------------------------------------");
foreach (word; dictionary.keys.sort)
{
writefln("%3d %s", dictionary[word], word);
}
return 0;
}
---------
)
Macros:
TITLE=Associative Arrays
WIKI=AssociativeArrays
DOLLAR=$
FOO=