4.15 Dictionaries
A dictionary is an instance of a datatype that maps keys to values. The following datatypes are all dictionaries:
vectors (using only exact integers as keys);
lists of pairs (an association list using equal? to compare keys); and
structures whose types implement the gen:dict generic interface.
(require racket/dict) | package: base |
4.15.1 Dictionary Predicates and Contracts
Beware that dict? is not a constant-time test on pairs, since checking that v is an association list may require traversing the list.
> (dict? #hash((a . "apple"))) #t
> (dict? '#("apple" "banana")) #t
> (dict? '("apple" "banana")) #f
> (dict? '((a . "apple") (b . "banana"))) #t
procedure
(dict-implements? d sym ...) → boolean?
d : dict? sym : symbol?
> (dict-implements? (hash 'a "apple") 'dict-set!) #f
> (dict-implements? (make-hash '((a . "apple") (b . "banana"))) 'dict-set!) #t
> (dict-implements? (make-hash '((b . "banana") (a . "apple"))) 'dict-remove!) #t
> (dict-implements? (vector "apple" "banana") 'dict-set!) #t
> (dict-implements? (vector 'a 'b) 'dict-remove!) #f
> (dict-implements? (vector 'a "apple") 'dict-set! 'dict-remove!) #f
procedure
(dict-implements/c sym ...) → flat-contract?
sym : symbol?
procedure
(dict-mutable? d) → boolean?
d : dict?
Equivalent to (dict-implements? d 'dict-set!).
> (dict-mutable? #hash((a . "apple"))) #f
> (dict-mutable? (make-hash)) #t
> (dict-mutable? '#("apple" "banana")) #f
> (dict-mutable? (vector "apple" "banana")) #t
> (dict-mutable? '((a . "apple") (b . "banana"))) #f
procedure
d : dict?
Equivalent to (or (dict-implements? d 'dict-remove!) (dict-implements? d 'dict-remove)).
> (dict-can-remove-keys? #hash((a . "apple"))) #t
> (dict-can-remove-keys? '#("apple" "banana")) #f
> (dict-can-remove-keys? '((a . "apple") (b . "banana"))) #t
procedure
d : dict?
Equivalent to (dict-implements? d 'dict-set).
> (dict-can-functional-set? #hash((a . "apple"))) #t
> (dict-can-functional-set? (make-hash)) #f
> (dict-can-functional-set? '#("apple" "banana")) #f
> (dict-can-functional-set? '((a . "apple") (b . "banana"))) #t
4.15.2 Generic Dictionary Interface
syntax
> (struct alist (v) #:methods gen:dict [(define (dict-ref dict key [default (lambda () (error "key not found" key))]) (cond [(assoc key (alist-v dict)) => cdr] [else (if (procedure? default) (default) default)])) (define (dict-set dict key val) (alist (cons (cons key val) (alist-v dict)))) (define (dict-remove dict key) (define al (alist-v dict)) (alist (remove* (filter (λ (p) (equal? (car p) key)) al) al))) (define (dict-count dict) (length (remove-duplicates (alist-v dict) #:key car)))]) ; etc. other methods > (define d1 (alist '((1 . a) (2 . b)))) > (dict? d1) #t
> (dict-ref d1 1) 'a
> (dict-remove d1 1) #<alist>
value
dict-set!, or #f if unsupported
dict-set, or #f if unsupported
dict-remove!, or #f if unsupported
dict-remove, or #f if unsupported
4.15.2.1 Primitive Dictionary Methods
These methods of gen:dict have no fallback implementations; they are only supported for dictionary types that directly implement them.
procedure
dict : dict? key : any/c
failure-result : (failure-result/c any/c) = (lambda () (raise (make-exn:fail ....)))
If failure-result is a procedure, it is called (through a tail call) with no arguments to produce the result.
Otherwise, failure-result is returned as the result.
> (dict-ref #hash((a . "apple") (b . "beer")) 'a) "apple"
> (dict-ref #hash((a . "apple") (b . "beer")) 'c) hash-ref: no value found for key
key: 'c
> (dict-ref #hash((a . "apple") (b . "beer")) 'c #f) #f
> (dict-ref '((a . "apple") (b . "banana")) 'b) "banana"
> (dict-ref #("apple" "banana") 1) "banana"
> (dict-ref #("apple" "banana") 3 #f) #f
> (dict-ref #("apple" "banana") -3 #f) dict-ref: contract violation
expected: exact-nonnegative-integer?
given: -3
in: the k argument of
(->i
((d dict?) (k (d) (dict-key-contract d)))
((default any/c))
any)
contract from: <collects>/racket/dict.rkt
blaming: top-level
(assuming the contract is correct)
at: <collects>/racket/dict.rkt:181.2
> (define h (make-hash)) > (dict-set! h 'a "apple") > h '#hash((a . "apple"))
> (define v (vector #f #f #f)) > (dict-set! v 0 "apple") > v '#("apple" #f #f)
procedure
(dict-set dict key v) → (and/c dict? immutable?)
dict : (and/c dict? immutable?) key : any/c v : any/c
> (dict-set #hash() 'a "apple") '#hash((a . "apple"))
> (dict-set #hash((a . "apple") (b . "beer")) 'b "banana") '#hash((a . "apple") (b . "banana"))
> (dict-set '() 'a "apple") '((a . "apple"))
> (dict-set '((a . "apple") (b . "beer")) 'b "banana") '((a . "apple") (b . "banana"))
procedure
(dict-remove! dict key) → void?
dict : (and/c dict? (not/c immutable?)) key : any/c
> (define h (make-hash)) > (dict-set! h 'a "apple") > h '#hash((a . "apple"))
> (dict-remove! h 'a) > h '#hash()
procedure
(dict-remove dict key) → (and/c dict? immutable?)
dict : (and/c dict? immutable?) key : any/c
> (define h #hash()) > (define h (dict-set h 'a "apple")) > h '#hash((a . "apple"))
> (dict-remove h 'a) '#hash()
> h '#hash((a . "apple"))
> (dict-remove h 'z) '#hash((a . "apple"))
> (dict-remove '((a . "apple") (b . "banana")) 'a) '((b . "banana"))
procedure
(dict-iterate-first dict) → any/c
dict : dict?
> (dict-iterate-first #hash((a . "apple") (b . "banana"))) 0
> (dict-iterate-first #hash()) #f
> (dict-iterate-first #("apple" "banana")) 0
> (dict-iterate-first '((a . "apple") (b . "banana"))) #<assoc-iter>
procedure
(dict-iterate-next dict pos) → any/c
dict : dict? pos : any/c
> (define h #hash((a . "apple") (b . "banana"))) > (define i (dict-iterate-first h)) > i 0
> (dict-iterate-next h i) 1
> (dict-iterate-next h (dict-iterate-next h i)) #f
procedure
(dict-iterate-key dict pos) → any
dict : dict? pos : any/c
> (define h '((a . "apple") (b . "banana"))) > (define i (dict-iterate-first h)) > (dict-iterate-key h i) 'a
> (dict-iterate-key h (dict-iterate-next h i)) 'b
procedure
(dict-iterate-value dict pos) → any
dict : dict? pos : any/c
> (define h '((a . "apple") (b . "banana"))) > (define i (dict-iterate-first h)) > (dict-iterate-value h i) "apple"
> (dict-iterate-value h (dict-iterate-next h i)) "banana"
4.15.2.2 Derived Dictionary Methods
These methods of gen:dict have fallback implementations in terms of the other methods; they may be supported even by dictionary types that do not directly implement them.
procedure
(dict-has-key? dict key) → boolean?
dict : dict? key : any/c
Supported for any dict that implements dict-ref.
> (dict-has-key? #hash((a . "apple") (b . "beer")) 'a) #t
> (dict-has-key? #hash((a . "apple") (b . "beer")) 'c) #f
> (dict-has-key? '((a . "apple") (b . "banana")) 'b) #t
> (dict-has-key? #("apple" "banana") 1) #t
> (dict-has-key? #("apple" "banana") 3) #f
> (dict-has-key? #("apple" "banana") -3) #f
procedure
(dict-set*! dict key v ... ...) → void?
dict : (and/c dict? (not/c immutable?)) key : any/c v : any/c
Supported for any dict that implements dict-set!.
> (define h (make-hash)) > (dict-set*! h 'a "apple" 'b "banana") > h '#hash((b . "banana") (a . "apple"))
> (define v1 (vector #f #f #f)) > (dict-set*! v1 0 "apple" 1 "banana") > v1 '#("apple" "banana" #f)
> (define v2 (vector #f #f #f)) > (dict-set*! v2 0 "apple" 0 "banana") > v2 '#("banana" #f #f)
procedure
(dict-set* dict key v ... ...) → (and/c dict? immutable?)
dict : (and/c dict? immutable?) key : any/c v : any/c
Supported for any dict that implements dict-set.
> (dict-set* #hash() 'a "apple" 'b "beer") '#hash((a . "apple") (b . "beer"))
> (dict-set* #hash((a . "apple") (b . "beer")) 'b "banana" 'a "anchor") '#hash((a . "anchor") (b . "banana"))
> (dict-set* '() 'a "apple" 'b "beer") '((a . "apple") (b . "beer"))
> (dict-set* '((a . "apple") (b . "beer")) 'b "banana" 'a "anchor") '((a . "anchor") (b . "banana"))
> (dict-set* '((a . "apple") (b . "beer")) 'b "banana" 'b "ballistic") '((a . "apple") (b . "ballistic"))
Supported for any dict that implements dict-ref and dict-set!.
> (dict-ref! (make-hasheq '((a . "apple") (b . "beer"))) 'a #f) "apple"
> (dict-ref! (make-hasheq '((a . "apple") (b . "beer"))) 'c 'cabbage) 'cabbage
> (define h (make-hasheq '((a . "apple") (b . "beer")))) > (dict-ref h 'c) hash-ref: no value found for key
key: 'c
> (dict-ref! h 'c (λ () 'cabbage)) 'cabbage
> (dict-ref h 'c) 'cabbage
procedure
(dict-update! dict key updater [ failure-result]) → void? dict : (and/c dict? (not/c immutable?)) key : any/c updater : (any/c . -> . any/c)
failure-result : (failure-result/c any/c) = (lambda () (raise (make-exn:fail ....)))
Supported for any dict that implements dict-ref and dict-set!.
> (define h (make-hash)) > (dict-update! h 'a add1) hash-update!: no value found for key: 'a
> (dict-update! h 'a add1 0) > h '#hash((a . 1))
> (define v (vector #f #f #f)) > (dict-update! v 0 not) > v '#(#t #f #f)
procedure
(dict-update dict key updater [failure-result])
→ (and/c dict? immutable?) dict : dict? key : any/c updater : (any/c . -> . any/c)
failure-result : (failure-result/c any/c) = (lambda () (raise (make-exn:fail ....)))
Supported for any dict that implements dict-ref and dict-set.
> (dict-update #hash() 'a add1) hash-update: no value found for key: 'a
> (dict-update #hash() 'a add1 0) '#hash((a . 1))
> (dict-update #hash((a . "apple") (b . "beer")) 'b string-length) '#hash((a . "apple") (b . 4))
Supported for any dict that implements dict-iterate-first, dict-iterate-next, dict-iterate-key, and dict-iterate-value.
Supported for any dict that implements dict-iterate-first, dict-iterate-next, dict-iterate-key, and dict-iterate-value.
> (dict-for-each #hash((a . "apple") (b . "banana")) (lambda (k v) (printf "~a = ~s\n" k v)))
a = "apple"
b = "banana"
procedure
(dict-empty? dict) → boolean?
dict : dict?
Supported for any dict that implements dict-iterate-first.
> (dict-empty? #hash((a . "apple") (b . "banana"))) #f
> (dict-empty? (vector)) #t
procedure
(dict-count dict) → exact-nonnegative-integer?
dict : dict?
Supported for any dict that implements dict-iterate-first and dict-iterate-next.
> (dict-count #hash((a . "apple") (b . "banana"))) 2
> (dict-count #("apple" "banana")) 2
Supported for any dict that implements dict-clear, dict-set!, dict-iterate-first, dict-iterate-next, dict-iterate-key, and dict-iterate-value.
> (define original (vector "apple" "banana")) > (define copy (dict-copy original)) > original '#("apple" "banana")
> copy '#("apple" "banana")
> (dict-set! copy 1 "carrot") > original '#("apple" "banana")
> copy '#("apple" "carrot")
procedure
(dict-clear dict) → dict?
dict : dict?
Supported for any dict that supports dict-remove, dict-iterate-first, dict-iterate-next, and dict-iterate-key.
> (dict-clear #hash((a . "apple") ("banana" . b))) '#hash()
> (dict-clear '((1 . two) (three . "four"))) '()
procedure
(dict-clear! dict) → void?
dict : dict?
Supported for any dict that supports dict-remove!, dict-iterate-first, and dict-iterate-key.
> (define table (make-hash)) > (dict-set! table 'a "apple") > (dict-set! table "banana" 'b) > table '#hash(("banana" . b) (a . "apple"))
> (dict-clear! table) > table '#hash()
Supported for any dict that implements dict-iterate-first, dict-iterate-next, and dict-iterate-key.
procedure
(dict-values dict) → list?
dict : dict?
Supported for any dict that implements dict-iterate-first, dict-iterate-next, and dict-iterate-value.
> (define h #hash((a . "apple") (b . "banana"))) > (dict-values h) '("apple" "banana")
procedure
(dict->list dict) → list?
dict : dict?
Supported for any dict that implements dict-iterate-first, dict-iterate-next, dict-iterate-key, and dict-iterate-value.
> (define h #hash((a . "apple") (b . "banana"))) > (dict->list h) '((a . "apple") (b . "banana"))
4.15.3 Dictionary Sequences
Supported for any dict that implements dict-iterate-first, dict-iterate-next, dict-iterate-key, and dict-iterate-value.
> (define h #hash((a . "apple") (b . "banana")))
> (for/list ([(k v) (in-dict h)]) (format "~a = ~s" k v)) '("a = \"apple\"" "b = \"banana\"")
procedure
(in-dict-keys dict) → sequence?
dict : dict?
Supported for any dict that implements dict-iterate-first, dict-iterate-next, and dict-iterate-key.
> (define h #hash((a . "apple") (b . "banana")))
> (for/list ([k (in-dict-keys h)]) k) '(a b)
procedure
(in-dict-values dict) → sequence?
dict : dict?
Supported for any dict that implements dict-iterate-first, dict-iterate-next, and dict-iterate-value.
> (define h #hash((a . "apple") (b . "banana")))
> (for/list ([v (in-dict-values h)]) v) '("apple" "banana")
procedure
(in-dict-pairs dict) → sequence?
dict : dict?
Supported for any dict that implements dict-iterate-first, dict-iterate-next, dict-iterate-key, and dict-iterate-value.
> (define h #hash((a . "apple") (b . "banana")))
> (for/list ([p (in-dict-pairs h)]) p) '((a . "apple") (b . "banana"))
4.15.4 Contracted Dictionaries
(list dict-vector (vector type-key-contract type-value-contract type-iter-contract instance-key-contract instance-value-contract instance-iter-contract))
The first vector must be a vector of 10 procedures which match the gen:dict generic interface (in addition, it must be an immutable vector). The second vector must contain six elements; each of the first three is a contract for the dictionary type’s keys, values, and positions, respectively. Each of the second three is either #f or a procedure used to extract the contract from a dictionary instance.
procedure
(dict-key-contract d) → contract?
d : dict?
procedure
(dict-value-contract d) → contract?
d : dict?
procedure
(dict-iter-contract d) → contract?
d : dict?
4.15.5 Custom Hash Tables
syntax
(define-custom-hash-types name optional-predicate comparison-expr optional-hash-functions)
optional-predicate =
| #:key? predicate-expr optional-hash-functions =
| hash1-expr | hash1-expr hash2-expr
Defines seven names:
name? recognizes instances of the new type,
immutable-name? recognizes immutable instances of the new type,
mutable-name? recognizes mutable instances of the new type with strongly-held keys,
weak-name? recognizes mutable instances of the new type with weakly-held keys,
make-immutable-name constructs immutable instances of the new type,
make-mutable-name constructs mutable instances of the new type with strongly-held keys, and
make-weak-name constructs mutable instances of the new type with weakly-held keys.
The constructors all accept a dictionary as an optional argument, providing initial key/value pairs.
> (define-custom-hash-types string-hash #:key? string? string=? string-length)
> (define imm (make-immutable-string-hash '(("apple" . a) ("banana" . b))))
> (define mut (make-mutable-string-hash '(("apple" . a) ("banana" . b)))) > (dict? imm) #t
> (dict? mut) #t
> (string-hash? imm) #t
> (string-hash? mut) #t
> (immutable-string-hash? imm) #t
> (immutable-string-hash? mut) #f
> (dict-ref imm "apple") 'a
> (dict-ref mut "banana") 'b
> (dict-set! mut "banana" 'berry) > (dict-ref mut "banana") 'berry
> (equal? imm mut) #f
> (equal? (dict-remove (dict-remove imm "apple") "banana") (make-immutable-string-hash)) #t
procedure
(make-custom-hash-types eql? [ hash1 hash2 #:key? key? #:name name #:for who]) →
(any/c . -> . boolean?) (any/c . -> . boolean?) (any/c . -> . boolean?) (any/c . -> . boolean?) (->* [] [dict?] dict?) (->* [] [dict?] dict?) (->* [] [dict?] dict?)
eql? :
(or/c (any/c any/c . -> . any/c) (any/c any/c (any/c any/c . -> . any/c) . -> . any/c))
hash1 :
(or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) = (const 1)
hash2 :
(or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) = (const 1) key? : (any/c . -> . boolean?) = (const #true) name : symbol? = 'custom-hash who : symbol? = 'make-custom-hash-types
The comparison function eql? may accept 2 or 3 arguments. If it accepts 2 arguments, it given two keys to compare them. If it accepts 3 arguments and does not accept 2 arguments, it is also given a recursive comparison function that handles data cycles when comparing sub-parts of the keys.
The hash functions hash1 and hash2 may accept 1 or 2 arguments. If either hash function accepts 1 argument, it is applied to a key to compute the corresponding hash value. If either hash function accepts 2 arguments and does not accept 1 argument, it is also given a recursive hash function that handles data cycles when computing hash values of sub-parts of the keys.
The predicate key? must accept 1 argument and is used to recognize valid keys for the new dictionary type.
Produces seven values:
a predicate recognizing all instances of the new dictionary type,
a predicate recognizing immutable instances,
a predicate recognizing mutable instances,
a predicate recognizing weak instances,
a constructor for immutable instances,
a constructor for mutable instances, and
a constructor for weak instances.
See define-custom-hash-types for an example.
These procedures are deprecated; use define-custom-hash-types instead.