eXept Software AG Logo

Smalltalk/X Webserver

Documentation of class 'Dictionary':

Home

Documentation
www.exept.de
Everywhere
for:
[back]

Class: Dictionary


Inheritance:

   Object
   |
   +--Collection
      |
      +--Set
         |
         +--Dictionary
            |
            +--CacheDictionary
            |
            +--Comanche::HttpFormDictionary
            |
            +--Dolphin::LookupTable
            |
            +--GetOpt
            |
            +--IdentityDictionary
            |
            +--OrderedDictionary
            |
            +--PluggableDictionary
            |
            +--RBSearchDictionary
            |
            +--ResourcePack
            |
            +--TIFFReader::TIFFMetaData
            |
            +--VAST::LookupTable
            |
            +--WeakValueDictionary

Package:
stx:libbasic
Category:
Collections-Unordered
Version:
rev: 1.236 date: 2024/03/11 14:21:02
user: cg
file: Dictionary.st directory: libbasic
module: stx stc-classLibrary: libbasic

Description:


a Dictionary is (conceptionally) a set of Associations storing key-value pairs.
(The implementation uses two arrays to store the keys and values separately.)
Searching for an element is done using a hash into the key array.
Another way of looking at a dictionary is as an array that uses
arbitrary access keys (i.e. not just integers as arrays do).

Since the keys are unordered, no internal element order is defined
(i.e. enumerating them may return elements in any order - even changing
 over time).

Many methods for searching and hashing are inherited from Set.

In other programming languages, Dictionaries are called 'HashTables', 'HashArray', 'HashList' or 'Maps'.

[Instance variables:]

    keyArray        <Array>         (from Set) the keys

    valueArray      <Array>         the values ('valueArray at:index' corresponds
                                    to the value stored under 'keyArray at:index')

Performance hints:
  since the dictionary does not really store associations internally,
  it is less efficient, to store/retrieve associations. The reason is
  that these assocs are created temporarily in some extract methods.
  I.e. 'at:key put:value' is faster than 'add:anAssoc'
  and 'keysAndValuesDo:' is faster than 'associationsDo:' etc.

  If only symbols or smallIntegers are used as keys, use IdentityDictionaries
  for slightly better performance, since both hashing and comparison is faster.

  If you have a rough idea how big the dictionary is going to grow,
  create it using #new: instead of #new. Even if the size given is a
  poor guess (say half of the real size), there is some 20-30% performance
  win to expect, since many resizing operations are avoided when associations
  are added.

Special note:
  in previous versions, nil was not allowed as valid key
  This has been changed; internally, a special nil-key is used,
  which is converted back to nil whenever keys are accessed.

Implementation note:
  You may ask why two separate arrays are used here,
  instead of a single array with keys and values at alternating slot indices
  (as done in eg. MethodDictionary).
  The reason is that having all keys in one array makes the search a little bit faster,
  as we will get better caching behavior on machines with long cache lines
  (will have 4 keys in the cache, when the first key is fetched).
  In addition, due to searching sequentially after a cache collision,
  this makes the whole process of lookup a little faster.

  TODO: 
    verify this - this really depends a lot on the hit / miss ratio.  
    if there is not a miss, but a hit, having the value right below the key
    will result in faster fetch of the value then.
    A better strategy might be to store chunks of 2
    keys and 2 values in alternating groups.

copyright

COPYRIGHT (c) 1991 by Claus Gittinger All Rights Reserved This software is furnished under a license and may be used only in accordance with the terms of that license and with the inclusion of the above copyright notice. This software may not be provided or otherwise made available to, or used by, any other person. No title to or ownership of the software is hereby transferred.

Class protocol:

Compatibility-Squeak
o  newFrom: aDictionaryOrCollectionOfAssociations
return a new instance where associations are taken from the argument

Usage example(s):

     Dictionary newFrom:{#foo -> #Foo. #bar -> #Bar}

     Dictionary
        newFrom:(Dictionary withKeysAndValues:#('one' 1 'two' 2 'three' 3 'four' 4))

o  newFromPairs: aSequenceableCollection
Answer an instance of me associating (anArray at: i) to (anArray at: i+1)
for each odd i.
anArray must have an even number of entries.
cg: irritating name; I would assume that the elements are pairs

Usage example(s):

     Dictionary newFromPairs:#( 1 #(1 2 3) 'foo' 'bar' )

instance creation
o  decodeFromLiteralArray: anArray
create & return a new instance from information encoded in anArray.

o  forCaseInsensitiveKeys
return a Dictionary where string keys are compared case insensitive.
I.e. it will retrieve a value given the key in both upper and lower case.
Non string keys will be treated as usual.

Usage example(s):

     |d|
     d := Dictionary forCaseInsensitiveKeys.
     d at:'Hello' put:'world'.
     d at:1 put:'one'.
     d includesKey:'hElLo'.   
     d['hello'].     
     d[1]     

o  withAssociations: aCollectionOfAssociations
return a new instance where associations are taken from the argument

Usage example(s):

     Dictionary withAssociations:{ #'one'->1 .
                                   #'two'->2 .
                                   #'three'->3 .
                                   #'four'->4 }

o  withKeyValuePairs: aCollection
return a new instance where keys and values are taken from
elements of aCollection, which are pairs (2-element SeuqenceableCollection) of key and value

Usage example(s):

     Dictionary withKeyValuePairs:#( ('one' 1) ('two' 2) ('three' 3) ('four' 4))
     Dictionary withKeyValuePairs:#( ('one' 1) ('two' 2) ('three' 3) ('four' 4)) asSet

o  withKeys: keyArray andValues: valueArray
return a new instance where keys and values are taken from
the argumentArrays.
It is ok for the keyArray to have less elements than valueArray;
(i.e. only those first keys are defined)

Usage example(s):

     Dictionary 
            withKeys:#('one' 'two' 'three' 'four')
            andValues:#(1 2 3 4)

     Dictionary 
            withKeys:#('one' 'two' 'three' 'four')
            andValues:#(1 2 3 4 5 6 7)

o  withKeys: aCollection valueBlock: aOneArgBlock
return a Dictionary with keys from aCollection's elements,
using aOneArgBlock to generate the values from aCollection's elements.

Usage example(s):

     Dictionary withKeys:#(10 20 30 40 50 60 70 80 90) valueBlock:[:e| e asString]

o  withKeysAndValues: aSequenceableCollection
return a new instance where keys and values are taken from alternating
elements of aSequenceableCollection

Usage example(s):

     Dictionary withKeysAndValues:#('one' 1 'two' 2 'three' 3 'four' 4)     => Dictionary('two'->2 'three'->3 'one'->1 'four'->4)
     Dictionary withKeysAndValues:#('one' 1 'two' 2 'three' 3 'incomplete') => error

o  withValues: valueCollection andKeys: keyCollection
similar to withKeys:andValues:,
however, here the number of values determines when to stop the loop,
(whereas withKeys:andValues: enumerates the keys).
Thus, here we can have less values than possible keys.

Usage example(s):

dict will have 4 values:

     Dictionary 
        withValues:#(10 20 30 40) andKeys:#( a b c d e f g h i)

o  withValues: aCollection keyBlock: aOneArgBlock
return a Dictionary with values from aCollection's elements,
using aOneArgBlock to generate the keys from aCollection's elements.

Usage example(s):

     Dictionary withValues:#(10 20 30 40 50 60 70 80 90) keyBlock:[:e| e asString]


Instance protocol:

Compatibility-Dolphin
o  equals: aDictionary

Compatibility-VW5.4
o  case: what otherwise: exceptionValue
slow compatibilty for VW case (inlined there, but not here).
Should be rewritten to a construct which is inlined in ST/X

Usage example(s):

     (1 -> 'one') , (2 -> 'two') , (3 -> 'three')
     case:2 otherwise:['foo'] 

     (1 -> 'one') , (2 -> 'two') , (3 -> 'three')
     case:4 otherwise:['foo']  

o  contentsEquals: aDictionary
Anwer true if the receiver and aDictionary contain the same key/values.
(ignoring the classes)

Squeak Compatibility
o  hasKeyStartingWith: aString
( an extension from the stx:goodies/webServer/comanche package )

accessing
o  associationAt: aKey
return an association consisting of aKey and the element indexed
by aKey -
report an error, if no element is stored under aKey

o  associationAt: aKey ifAbsent: exceptionBlock
return an association consisting of aKey and the element indexed by aKey -
return result of exceptionBlock if no element is stored under aKey.
Warning: this is a comatibility interface only, with a different semantic as
the original ST80 implementation. The returned assoc is created on the fly,
and not the one stored in the receiver (there are not assocs there)

o  associations
return an ordered collection containing the receiver's associations.

o  at: aKey
return the element indexed by aKey - report an error if none found

o  at: aKey ifAbsent: exceptionBlock
return the element indexed by aKey -
return result of exceptionBlock if no element is stored under aKey

o  at: aKey ifAbsent: default update: aBlock
update the element stored under aKey with the result from
evaluating aBlock with the previous stored value as argument, or with default,
if there was no such key initially.
I.e. this is the same as
self at:aKey put:(aBlock value:(self at:aKey ifAbsent:default)).
Return the new value stored.
This is an optimized accessor, which only computes the hash value once.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one'  ifAbsent:0 update:[:val | val + 1].
     d at:'two'  ifAbsent:0 update:[:val | val + 1].
     d at:'three' ifAbsent:0  update:[:val | val + 1].
     d at:'two' ifAbsent:0  update:[:val | val + 1].
     d at:'three' ifAbsent:0  update:[:val | val + 1].
     d at:'three' ifAbsent:0  update:[:val | val + 1].
     d

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'two'  ifAbsent:0 update:[:val | val + 1].
     d at:'two'  ifAbsent:0 update:[:val | 1 to:30 do:[:idx| d at:idx printString put:idx]. val + 1].
     d

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'two'  ifAbsent:0 update:[:val | val + 1].
     d at:'two'  ifAbsent:0 update:[:val | d removeKey:'two'. val + 1].
     d

o  at: aKey ifAbsentPut: valueBlock
return the element indexed by aKey if present,
if not present, store the result of evaluating valueBlock
under aKey and return it.
WARNING: do not add elements while iterating over the receiver.
Iterate over a copy to do this.

Usage example(s):

     |d|

     d := Dictionary new.
     Transcript showCR:(d at:'foo' ifAbsentPut:'bar').
     Transcript showCR:(d at:'foo2' ifAbsentPut:'bar2').
     Transcript showCR:(d at:'foo' ifAbsentPut:'barX').
     Transcript showCR:(d at:'foo2' ifAbsentPut:'bar2X').

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one' ifAbsentPut:[d at:'one' put:1. 33333].
     d

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'two'  ifAbsentPut:[1 to:30 do:[:idx| d at:idx printString put:idx]. 2].
     d

o  at: aKey ifPresent: presentBlock ifAbsent: absentBlock
try to retrieve the value stored at aKey.
If there is nothing stored under this key,
return the value from absentBlock;
otherwise, answer the value from presentBlock, optionally passing the stored value.

Usage example(s):

     |d|
     d := Dictionary new.
     d at:#foo put:'yes this is foo'.

     d at:#foo 
        ifPresent:[:val | Transcript showCR:'the value of foo is: ',val. val]
        ifAbsent:[ Transcript showCR:'not present'. 123].

     d at:#bar 
        ifPresent:[:val | Transcript showCR:'the value of bar is: ',val. val]
        ifAbsent:[ Transcript showCR:'not present'. 123].

o  at: aKey put: anObject
add the argument anObject under key, aKey to the receiver.
Return anObject (sigh).
WARNING: do not add elements while iterating over the receiver.
Iterate over a copy to do this.

o  at: aKey put: anObject ifPresent: aBlock
if the receiver contains an element stored under aKey,
retrieve it and evaluate aBlock passing the element as argument,
returning the block's value.
If no element was stored under that key, store aValue under the key.
Use this with an error-reporting block, to ensure that no keys are reused

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'foo' put:1234 ifPresent:[:v| self error: 'duplicate: ', v printString ].
     d at:'foo' put:1234 ifPresent:[:v| self halt:'duplicate: ', v printString. 5555 ].

o  at: aKey update: aBlock
update the element stored under aKey with the result from
evaluating aBlock with the previous stored value as argument.
Report an error if there was no such key initially.
I.e. this is the same as self at:aKey put:(aBlock value:(self at:aKey)).
Return the new value stored.
This is an optimized accessor, which only computes the hash value once.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one'  update:[:val | val + 1].

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one' put:0.
     d at:'one'  update:[:val | d removeKey:'one'. val + 1].

Usage example(s):

     |d|
     d := Dictionary new.
     d at:'one' put:0.
     d at:'two' put:0.
     d at:'three' put:0.

     d at:'one'    update:[:val | val + 1].
     d at:'two'    update:[:val | val + 1].
     d at:'three'  update:[:val | val + 1].
     d at:'two'    update:[:val | val + 1].
     d at:'three'  update:[:val | val + 1].
     d at:'three'  update:[:val | val + 1].
     d

o  at: aKey update: aBlock ifAbsent: absentBlock
update the element stored under aKey with the result from
evaluating aBlock with the previous stored value as argument.
Evaluate absentBlock and return its value if there was no such key initially.
Otherwise return the new value stored.
This is an optimized accessor, which only computes the hash value once.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one'  update:[:val | val + 1] ifAbsent:'nope'.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one' put:0.
     d at:'one'  update:[:val | d removeKey:'one'. val + 1] ifAbsent:'nope'.

Usage example(s):

     |d|
     d := Dictionary new.
     d at:'one' put:0.
     d at:'two' put:0.
     d at:'three' put:0.

     d at:'one'    update:[:val | val + 1] ifAbsent:'nope'.
     d at:'two'    update:[:val | val + 1] ifAbsent:'nope' .
     d at:'three'  update:[:val | val + 1] ifAbsent:'nope'.
     d at:'two'    update:[:val | val + 1] ifAbsent:'nope'.
     d at:'three'  update:[:val | val + 1] ifAbsent:'nope'.
     d at:'three'  update:[:val | val + 1] ifAbsent:'nope'.
     d

o  atPath: aKeyArray ifAbsent: exceptionBlock
traverse nested dictionarie via the keys in keyArray, aSequenceableCollection.
Return the value at the last key,
or the result of exceptionBlock if no element can be found.

o  keyAtEqualValue: aValue
return the key whose value is equal (i.e. using #= for compare)
to the argument, nil if none found.
This is a slow access, since there is no fast reverse mapping.
NOTICE:
The value is searched using equality compare;
use #keyAtValue: to compare for identity.

o  keyAtEqualValue: aValue ifAbsent: exceptionBlock
return the key whose value is equal (i.e. using #= for compare)
to the argument, if not found, return the value of exceptionBlock.
This is a slow access, since there is no fast reverse mapping.
NOTICE:
The value is searched using equality compare;
use #keyAtValue:ifAbsent: to compare for identity.

o  keyAtIdenticalValue: aValue
for protocol compatibility only:
return the key whose value is identical (i.e. using #== for compare)
to the argument, nil if none found.
This is a slow access, since there is no fast reverse mapping.
NOTICE:
The value is searched using identity compare;
use #keyAtEqualValue: to compare for equality.

o  keyAtIdenticalValue: aValue ifAbsent: exceptionBlock
for protocol compatibility only:
return the key whose value is identical (i.e. using #== for compare)
to the argument, nil if none found.
This is a slow access, since there is no fast reverse mapping.
NOTICE:
The value is searched using identity compare;
use #keyAtEqualValue: to compare for equality.

o  keyAtIdentityValue: aValue
return the key whose value is identical (i.e. using #== for compare)
to the argument, nil if none found.
This is a slow access, since there is no fast reverse mapping.
NOTICE:
The value is searched using identity compare;
use #keyAtEqualValue: to compare for equality.

o  keyAtIdentityValue: aValue ifAbsent: exceptionBlock
return the key whose value is identical (i.e. using #== for compare)
to the argument, if not found, return the value of exceptionBlock.
This is a slow access, since there is no fast reverse mapping.
NOTICE:
The value is searched using identity compare;
use #keyAtEqualValue:ifAbsent: to compare for equality.

o  keyAtValue: aValue
return the key whose value is identical (i.e. using #== for compare)
to the argument, nil if none found.
This is a slow access, since there is no fast reverse mapping.
NOTICE:
The value is searched using identity compare;
use #keyAtEqualValue: to compare for equality.

o  keyAtValue: aValue ifAbsent: exceptionBlock
return the key whose value is identical (i.e. using #== for compare)
to the argument, if not found, return the value of exceptionBlock.
This is a slow access, since there is no fast reverse mapping
(receiver is searched sequentially).
NOTICE:
The value is searched using identity compare;
use #keyAtEqualValue:ifAbsent: to compare for equality.

o  keys
return a collection containing all keys of the receiver

o  newAt: key put: value
For compatibility with Trie::SmallDictionaryWithNElements.
Same operation as at:put:,
with the possibiliy of returning a new instance if there was a need to grow

adding & removing
o  add: anAssociation
add the argument, anAssociation to the receiver.
Returns the argument, anAssociation.

WARNING: do not add elements while iterating over the receiver.
Iterate over a copy to do this.

o  addAll: aCollection
ANSI 5.7.2.1:
Message: addAll: dictionary
Synopsis
Store the elements of dictionary in the receiver at the
corresponding keys from dictionary.
Definition: <abstractDictionary>
This message is equivalent to repeatedly sending the #at:put:
message to the receiver with each of the keys and elements in
dictionary in turn. If a key in dictionary is key equivalent
to a key in the receiver, the associated element in dictionary
replaces the element in the receiver.

Returns the argument, aCollection.

WARNING: do not add elements while iterating over the receiver.
Iterate over a copy to do this.

Usage example(s):

     |d1 d2|

     d1 := Dictionary new.
     d1 at:1 put:'one'.
     d1 at:2 put:'two'.
     d2 := Dictionary new.
     d2 at:3 put:'three'.
     d2 at:4 put:'four'.
     d1 addAll:d2.
     d1.

o  addOrReplace: anAssociation
add the argument, anAssociation to the receiver.
Returns the argument, anAssociation.

WARNING: do not add elements while iterating over the receiver.
Iterate over a copy to do this.

Usage example(s):

        self new
            add:(1 -> 'one');
            addOrReplace:(1.0 -> 'ONE')   ; yourself

o  addPairsFrom: aSequenceableCollectionOrDictionary
merge consecutive key-value pairs from aSequenceableCollection into the receiver.

Usage example(s):

     |d1 arr|

     d1 := Dictionary new.
     d1 at:1 put:'one'.
     d1 at:2 put:'two'.
     arr := #(3 'three'  4 'four').
     d1 addPairsFrom:arr.
     d1.

o  clearContents
remove all elements from the receiver, but do not resize.
Returns the receiver.
Similar to removeAll, but might behave better,
if the receiver is to be filled again afterwards.

o  declare: key from: aDictionary
if the receiver does not include an association for key,
take the association from aDictionary and add it to the receiver.
If aDictionary does not contain such an association, use nil
as the value of the new dictionary.

Stupidity Notice:
Incompatibility with #declareAllFrom:, where the other values are
defined unconditionally.

WARNING: do not add elements while iterating over the receiver.
Iterate over a copy to do this.

o  declareAll: keys from: aCollectionOrDictionary
declare all keys in the first argument, keys
from values taken from the second argument, aCollectionOrDictionary.
If aCollectionOrDictionary is a dictionary, access via the key;
if it is a sequencable collection, add corresponding values pairwise.
Values present in the arg will always end up in the receiver;
i.e. a value coming from the argument is already in the receiver,
the value from aDictionaryOrNil is stored into the receiver.

Usage example(s):

     |d|

     d := Dictionary new.
     d declareAll:#(a b c) from:#(10 20 30).
     d.

Usage example(s):

     |d1 d2 d3|

     d1 := Dictionary new.
     d1 declareAll:#(a b c) from:#(10 20 30).
     d2 := Dictionary new.
     d2 declareAll:#( b c d) from:#(100 200 300).
     d3 := Dictionary new.
     d3 declareAll:#(a b c) from:d1.
     d3 declareAll:#(c d) from:d2.
     d3

o  declareAllFrom: aDictionaryOrNil
merge all key-value pairs from aDictionary into the receiver.
Values present in the arg will always end up in the receiver;
i.e. even if a value coming from the argument is already in the receiver,
the value from aDictionaryOrNil is stored into the receiver.

sigh:
For compatibility with #declare:from: the behavior should be changed as following:
If the receiver already contains a key, the existing value is retained.
To keep the compatibility with other smalltalks, the semantics of this remains
as is, and #declareAllNewFrom: was added for convenience.
See #declareAllNewFrom: which does exactly what this name implies.

o  declareAllFrom: aDictionaryOrNil mapping: mapper
merge all key-mapped-value pairs from aDictionary into the receiver.
Values read from aDictionaryOrNil are first mapped via the mapper block,
then declared in the receiver

Usage example(s):

     |d1 d2|

     d1 := Dictionary new.
     d1 at:'one' put:1.
     d1 at:'two' put:2.
     d1 at:'three' put:3.
     d1 at:'four' put:4.

     d2 := Dictionary new.
     d2 declareAllFrom:d1 mapping:[:old | old squared].
     d2

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one' put:1.
     d at:'two' put:2.
     d at:'three' put:3.
     d at:'four' put:4.

     d declareAllFrom:d mapping:[:old | old squared].
     d     

o  declareAllNewFrom: aDictionaryOrNil
merge all new key-value pairs from aDictionary into the receiver
i.e. If the receiver already contains a key, the existing value is retained.
See also #declareAllFrom:

o  dropAllSuchThat: conditionBlockOnValueOrKeyAndValue
Apply the condition block to each key and value (if it is a 2-arg block)
or to each value (if it is a one arg block)
and drop the entry (key and value) if the condition is true.
Returns the receiver.
Differs from #removeAllSuchThat:
returns self instead of the removed associations.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one' put:1.
     d at:'two' put:2.
     d at:'three' put:3.
     d at:'four' put:4.
     d at:'uno' put:1.
     d at:'due' put:2.
     d at:'tre' put:3.
     d at:'eins' put:1.
     d at:'zwei' put:2.
     d at:'drei' put:3.

     d dropAllSuchThat:[:k :v | k startsWith:'t'].
     d dropAllSuchThat:[:k :v | v = 2].
     d inspect

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'one' put:1.
     d at:'two' put:2.
     d at:'three' put:3.
     d at:'four' put:4.
     d at:'uno' put:1.
     d at:'due' put:2.
     d at:'tre' put:3.
     d at:'eins' put:1.
     d at:'zwei' put:2.
     d at:'drei' put:3.

     d dropAllSuchThat:[:v | v = 2].
     d inspect

o  remove: oldObject ifAbsent: aBlock
remove oldObject from the collection and return it.
If it was not in the collection return the value of aBlock.

This is blocked here; you have to use one of
#removeKey:, #saveRemoveKey:, #removeAssociation:,
#removeValue: or #safeRemoveValue:

o  removeAllKeys: aKeyCollection
remove all associations under each key in aKeyCollection from the dictionary.
If one was not in the collection, report an error.

WARNING: do not remove elements while iterating over the receiver.
See #safeRemoveKey: to do this.

o  removeAllKeys: aKeyCollection ifAbsent: aBlock
remove all associations under each key in aKeyCollection from the collection.
If it was not in the collection return the result from evaluating aBlock
(invoked for each missing element).

WARNING: do not remove elements while iterating over the receiver.
See #safeRemoveKey: to do this.

o  removeAllSuchThat: conditionBlockOnValueOrKeyAndValue
Apply the condition block to each key and value (if it is a 2-arg block)
or to each value (if it is a one arg block)
and remove the entry (key and value) if the condition is true.
Return a collection of removed associations.
Differs from #dropAllSuchThat:
dropAllSuchThat: returns self instead of the removed associations.

Usage example(s):

     |d removed|

     d := Dictionary new.
     d at:'one' put:1.
     d at:'two' put:2.
     d at:'three' put:3.
     d at:'four' put:4.
     d at:'uno' put:1.
     d at:'due' put:2.
     d at:'tre' put:3.
     d at:'eins' put:1.
     d at:'zwei' put:2.
     d at:'drei' put:3.

     removed := d removeAllSuchThat:[:k :v | k startsWith:'t'].
     self assert:(removed keys asOrderedCollection sorted sameContentsAs:#('three'  'tre' 'two') sorted).

     d declareAllFrom:removed.

     removed := d removeAllSuchThat:[:k :v | v = 2].
     self assert:(removed keys asOrderedCollection sorted sameContentsAs:#('two'  'zwei' 'due') sorted).

o  removeAssociation: assoc
remove the association from the collection.
If it was not in the collection report an error.
Only the key is used in the passed argument, and a new
association, for the key and the previously stored value is returned.

WARNING: do not remove elements while iterating over the receiver.
See #safeRemoveKey: to do this.

o  removeIdentityValue: aValue ifAbsent: aBlock
remove (first) the association to aValue from the collection,
return the key under which it was stored previously.
If it was not in the collection return result from evaluating aBlock.
The value is searched using identity compare.

Notice, this does a linear search through the values and may
therefore be slow for big dictionaries.

WARNING: do not remove elements while iterating over the receiver.
See #safeRemoveValue: to do this.

o  removeKey: aKey
remove the association under aKey from the collection,
return the value previously stored there.
If it was not in the collection report an error.

WARNING: do not remove elements while iterating over the receiver.
See #safeRemoveKey: to do this.

o  removeKey: aKey ifAbsent: aBlock
remove the association under aKey from the collection,
return the value previously stored there.
If it was not in the collection return the result
from evaluating aBlock.

WARNING: do not remove elements while iterating over the receiver.
See #safeRemoveKey: to do this.

o  removeKeysWithValue: valueToRemove
remove all associations where the value equals valueToRemove.
Does not report an error there was no such value.

This does not resize the underlying collection
and therefore does NOT rehash & change the elements order.
Therefore this can be used while enumerating the receiver,
which is not possible if #removeKey: is used.
You may want to manually resize the receiver using #possiblyShrink
after the enumeration.

Usage example(s):

     |d|
     d := Dictionary new.
     d at:'a' put:1.
     d at:'b' put:2.
     d at:'c' put:1.0.
     d at:'d' put:3.
     d at:'e' put:2.
     d at:'f' put:4.
     d at:'g' put:5.
     d removeKeysWithValue:1.
     self assert:(d includesKey:'a') not.
     self assert:(d includesKey:'c') not.
     self assert:(#('b' 'd' 'e' 'f' 'g') conform:[:k | d includesKey:k]).
     d removeKeysWithValue:2.
     self assert:(d includesKey:'b') not.
     self assert:(d includesKey:'e') not.
     self assert:(#( 'd' 'f' 'g') conform:[:k | d includesKey:k]).
     d inspect.
     d possiblyShrink.
     d inspect.

Usage example(s):

     |d|
     d := OrderedDictionary new.
     d at:'a' put:1.
     d at:'b' put:2.
     d at:'c' put:1.0.
     d at:'d' put:3.
     d at:'e' put:2.
     d at:'f' put:4.
     d at:'g' put:5.
     d removeKeysWithValue:1.
     self assert:(d includesKey:'a') not.
     self assert:(d includesKey:'c') not.
     self assert:(#('b' 'd' 'e' 'f' 'g') conform:[:k | d includesKey:k]).
     d removeKeysWithValue:2.
     self assert:(d includesKey:'b') not.
     self assert:(d includesKey:'e') not.
     self assert:(#( 'd' 'f' 'g') conform:[:k | d includesKey:k]).
     d inspect.
     d possiblyShrink.
     d inspect.

o  removeValue: aValue
remove (first) the association to aValue from the collection,
return the key under which it was stored previously.
If it was not in the collection, report an error.
The value is searched using equality compare here,
but identity compare in the IdentityDictionary subclass.

Notice, this does a linear search through the values and may
therefore be slow for big dictionaries.

WARNING: do not remove elements while iterating over the receiver.
See #safeRemoveValue: to do this.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:1 put:'one'.
     d at:2 put:'two'.
     d at:3 put:'three'.
     d removeValue:'two'.
     d

o  removeValue: aValue ifAbsent: aBlock
remove (first) the association to aValue from the collection,
return the key under which it was stored previously.
If it was not in the collection return result from evaluating aBlock.
The value is searched using equality compare here,
but identity compare in the IdentityDictionary subclass.

Notice, this does a linear search through the values and may
therefore be slow for big dictionaries.

WARNING: do not remove elements while iterating over the receiver.
See #safeRemoveValue: to do this.

o  safeRemoveKey: aKey
remove the association under aKey from the collection.
Return the value previously stored there.
If it was not in the collection return nil.

In contrast to #removeKey:, this does not resize the underlying collection
and therefore does NOT rehash & change the elements order.
Therefore this can be used while enumerating the receiver,
which is not possible if #removeKey: is used.

WARNING: since no resizing is done, the physical amount of memory used
by the container remains the same, although the logical size shrinks.
You may want to manually resize the receiver using #possiblyShrink
after the enumeration.

o  safeRemoveValue: aValue
remove the (first) association to aValue from the collection,
return the key under which it was stored previously.
If it was not in the collection return nil.
The value is searched using equality compare here,
but identity compare in the IdentityDictionary subclass.

In contrast to #removeValue:, this does not resize the underlying collection
and therefore does NOT rehash & change the elements order.
Therefore, this can be used while enumerating the receiver,
which is not possible if #removeValue: is used.

WARNING: since no resizing is done, the physical amount of memory used
by the container remains the same, although the logical size shrinks.
You may want to manually resize the receiver using #possiblyShrink
after the enumeration.

o  testAndAdd: anAssociation
add anAssociation to myself, if anAssociation's key is not already present.
Answer true, if anAssociation's key was already in the collection.

Usage example(s):

        |dict|

        dict := Dictionary withKeys:Symbol allSymbols valueBlock:[:k| k].
        TimeDuration toRun:[
            1 to:1000000 do:[:i|
                dict testAndAdd:((i asString, 'xxxxxxxxxxxxxxxxxxxxxxxxxx') -> 0).
                dict testAndAdd:('xxxxxxxxxxxxxxxxxxxxxxxxxx' -> 0).
            ]
        ]

comparing
o  = aDictionary
return true, if the argument is a Dictionary containing the same
key-value pairs as I do

Usage example(s):

     |d1 d2|

     d1 := Dictionary new.
     d2 := Dictionary new.
     d1 at:1 put:'one'.
     d1 at:'one' put:1.
     d1 at:2 put:#two.
     d1 at:'two' put:2.

     d2 at:1 put:'one'.
     d2 at:'one' put:1.
     d2 at:2 put:#two.
     d2 at:'two' put:2.
     d1 = d2

Usage example(s):

     |d1 d2|

     d1 := Dictionary new.
     d2 := Dictionary new.
     d1 at:1 put:'uno'.
     d1 at:'one' put:1.
     d1 at:2 put:#two.
     d1 at:'two' put:2.

     d2 at:1 put:'one'.
     d2 at:'one' put:1.
     d2 at:2 put:#two.
     d2 at:'two' put:2.
     d1 = d2

Usage example(s):

     |d1 d2|

     d1 := Dictionary new.
     d2 := Dictionary new.
     d1 at:10 put:'one'.
     d1 at:'one' put:1.
     d1 at:2 put:#two.
     d1 at:'two' put:2.

     d2 at:1 put:'one'.
     d2 at:'one' put:1.
     d2 at:2 put:#two.
     d2 at:'two' put:2.
     d1 = d2

o  sameContentsAs: aDictionary
answer true, if all the elements in self and aCollection
are common, i.e. aDictionary contains the same
key-value pairs as I do

converting
o  asArrayWithKeysAndValues
return an array with alternating keys and values

Usage example(s):

      |dict|

      dict := Dictionary new.
      dict at:1 put:'bla'.
      dict at:'fasel' put:#[1 2 3 4].
      dict asArrayWithKeysAndValues

o  asDictionary
I am what I am

o  asKeysAndValues
(comment from inherited method)
return a Dictionary with the receiver's associations as key->value pairs
using each element's key as dictionary key and value as dictionary value.

o  asNewDictionary
return myself as a unique new dictionary

o  associationsOrderedBy: aCollectionOfKeys
return an OrderedCollection of my key-value pairs, ordered by the given key list

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'zzz' put:3.
     d at:'aaa' put:1.
     d at:'eee' put:2.
     d.
     d valuesOrderedBy:#('aaa' 'eee' 'zzz').
     d associationsOrderedBy:#('aaa' 'eee' 'zzz').

o  fromLiteralArrayEncoding: encoding
read my values from an encoding.
The encoding is supposed to be of the form:
(Dictionary key1 val1 ... keyN valN)

Usage example(s):

     Dictionary new fromLiteralArrayEncoding:#(Dictionary 'hello' 'world' 1 'foo')

o  literalArrayEncoding
|dict|

dict := Dictionary new.
dict at:1 put:'bla'.
dict at:'fasel' put:#[1 2 3 4].
dict literalArrayEncoding

o  valuesOrderedBy: aCollectionOfKeys
return an OrderedCollection of my values, ordered by the given key list

copying
o  , anotherDictionaryOrAssociation
return a new dictionary containing a merged set of associations.
If anotherDictionaryOrAssociation includes any of the receiver's keys,
the value from anotherDictionaryOrAssociation will be placed into the
returned result.

Usage example(s):

     |d1 d2|

     d1 := Dictionary new.
     d1 at:#a put:'aaa'.
     d1 at:#b put:'bbb'.

     d2 := Dictionary new.
     d2 at:#b put:'bbbb'.
     d2 at:#c put:'ccc'.

     d1 , d2

Usage example(s):

     |d1 d2|

     d1 := Dictionary new.
     d1 at:#a put:'aaa'.
     d1 at:#b put:'bbb'.
     d2 := d1 , (#c -> 'ccc').
     d2

Usage example(s):

     |d1 d2|

     d1 := (#a -> 10) , (#b -> 20).
     d2 := d1 , (#c -> 30).
     d1 inspect.
     d2 inspect.

o  copyWithout: anAssociation
Return a copy of the dictionary that is 1 smaller than the receiver and
does not include the argument, anAssociation
No error is reported, if elementToSkip is not in the collection.

Usage example(s):

     |d d2|

     d := Dictionary new
            at:1 put:'1';
            at:2 put:'2';
            at:3 put:'3';
            at:4 put:'4';
            at:5 put:'5';
            yourself.
     d2 := d copyWithout:(4->'4').
     d2   

     |d d2|

     d := OrderedDictionary new
            at:1 put:'1';
            at:2 put:'2';
            at:3 put:'3';
            at:4 put:'4';
            at:5 put:'5';
            yourself.
     d2 := d copyWithout:(4->'4').
     d2      

copying-private
o  postCopy
have to copy the valueArray too

encoding & decoding
o  readJSONContentsFrom: aJSONObject

enumerating
o  allKeysDo: aBlock
perform the block for all keys in the collection.
Obsolete: use keysDo: for ST-80 compatibility.

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  associationsCollect: aBlock
for each key-value pair in the receiver, evaluate the argument, aBlock
and return a collection with the results.

See also:
#keysAndValuesCollect: (which passes separate keys & values)
#collect: (which only passes values)

This is much like #keysAndValuesCollect:, but aBlock gets the
key and value as a single association argument.
#keysAndValuesCollect: and is a bit faster therefore (no intermediate objects).

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

Usage example(s):

     |ages|

     ages := Dictionary new.
     ages at:'cg' put:37.
     ages at:'ca' put:33.
     ages at:'sv' put:36.
     ages at:'tk' put:28.
     ages associationsCollect:[:assoc |
                assoc key , '''s age is ' , assoc value printString]

o  associationsDo: aBlock
perform the block for all associations in the collection.

See also:
#do: (which passes values to its block)
#keysDo: (which passes only keys to its block)
#keysAndValuesDo: (which passes keys&values)

This is much like #keysAndValuesDo:, but aBlock gets the
key and value as a single association argument.
#keysAndValuesDo: and is a bit faster therefore (no intermediate objects).

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

o  associationsDo: aBlock separatedBy: sepBlock
perform the block for all associations in the collection.

See also:
#do: (which passes values to its block)
#keysDo: (which passes only keys to its block)
#keysAndValuesDo: (which passes keys&values)

This is much like #keysAndValuesDo:, but aBlock gets the
key and value as a single association argument.
#keysAndValuesDo: and is a bit faster therefore (no intermediate objects).

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

o  associationsReverseDo: aBlock
perform the block for all associations in the collection.
Since dictionary does not define any order of its elements,
this is the same as #associationsDo: here.
Provided for protocol compatibility with OrderedDictionary

o  associationsSelect: aBlock
return a new collection with all elements from the receiver, for which
the argument aBlock evaluates to true.
The block gets keys and values as an association argument.

See also: #keysAndValuesSelect: (which is slightly faster),
#select: (which only passes the value)

This is much like #keysAndValuesSelect:, but aBlock gets the
key and value as a single association argument.
#keysAndValuesSelect: and is a bit faster therefore (no intermediate objects).

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

Usage example(s):

     |ages|

     ages := Dictionary new.
     ages at:'cg' put:37.
     ages at:'ca' put:33.
     ages at:'sv' put:36.
     ages at:'tk' put:28.

     ages associationsSelect:[:assoc |
		(assoc key startsWith:'c') or:[assoc value < 30]].

o  do: aBlock
evaluate the block for all values in the collection.
Notice: the order of evaluation is not defined (and may appear random, actually)

See also:
#associationsDo: (which passes key-value associations)
#keysAndValuesDo: (which passes keys & values separately)
#keysDo: (which passes keys only)

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

o  flatCollect: aBlock
Evaluate aBlock for each of the receiver's values (by opposition to keys) and answer the
list of all resulting values flatten one level.
Assumes that aBlock returns some kind of collection for each element.
Equivalent to the lisp's mapcan

o  keysAndValuesDo: aTwoArgBlock
evaluate the argument, aBlock for every element in the collection,
passing both key and element as arguments.

See also:
#associationsDo: (which passes keys->value pairs)
#do: (which only passes values)
#keysDo: (which only passes keys)

This is much like #associationsDo:, but aBlock gets the
key and value as two separate arguments.
#associationsDo: is a bit slower.

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

o  keysAndValuesSelect: aBlock
return a new collection with all elements from the receiver, for which
the argument aBlock evaluates to true.
The block gets keys and values as separate arguments.

See also:
#associationsSelect: (which passes key-value pairs),
#keysSelect: (which passes key values),
#select: (which only passes the value)

This is much like #associationsSelect:, but aBlock gets the
key and value as two separate arguments.
#associationsSelect: is a bit slower.

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

Usage example(s):

     |ages|

     ages := Dictionary new.
     ages at:'cg' put:37.
     ages at:'ca' put:33.
     ages at:'sv' put:36.
     ages at:'tk' put:28.

     ages keysAndValuesSelect:[:name :age |
		(name startsWith:'c') or:[age < 30]].

o  keysDo: aBlock
evaluate the argument, aBlock for every key in the collection.

See also:
#associationsDo: (which passes key-value associations)
#keysAndValuesDo: (which passes keys & values separately)
#do: (which passes values only)

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

o  keysSelect: aBlock
return a new collection with all elements from the receiver, for which
the argument aBlock evaluates to true.
The block gets the individual keys as its single argument.

See also:
#associationsSelect: (which passes key->value associations),
#keysAndValuesSelect: (which passes key & value args)
#select: (which passes values as arg),

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:#foo put:#bar.
     d at:#bar put:#baz.
     d at:#baz put:#foo.

     d keysSelect:[:el | el startsWith:'b'].

o  select: aBlock
return a new collection with all elements from the receiver, for which
the argument aBlock evaluates to true.
The block gets the individual values as its single argument.

See also:
#associationsSelect: (which passes key->value associations),
#keysAndValuesSelect: (which passes key & value args)
#keysSelect: (which passes key values),

WARNING: do not add/remove elements while iterating over the receiver.
Iterate over a copy to do this.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:#foo put:#bar.
     d at:#bar put:#baz.
     d at:#baz put:#foo.

     d select:[:el | el startsWith:'b'].

o  valuesDo: aBlock
perform the block for all values in the collection.
Same as #do: - for VisualWorks compatibility

o  xor: aCollection
return a new set containing all elements,
which are contained in either the receiver or aCollection, but not in both.

Usage example(s):

     (Dictionary withKeysAndValues:#(1 'uno' 2 'due' 3 'tre' 4 'quatro'))
        xor:(Dictionary withKeysAndValues:#(1 'uno'  4 'quatro' 5 'cinque'))

inspecting
o  inspector2TabLabel
( an extension from the stx:libtool package )
label of the main tab

o  inspectorClass
( an extension from the stx:libtool package )
redefined to use DictionaryInspector
(instead of the default Inspector).

o  inspectorExtraAttributes
( an extension from the stx:libtool package )
extra (pseudo instvar) entries to be shown in an inspector.

o  inspectorValueListIconFor: anInspector
( an extension from the stx:libtool package )
returns the icon to be shown alongside the value list of an inspector

printing & storing
o  printElementsDo: aBlock
redefined, so #printOn: prints associations

o  storeOn: aStream
output a printed representation (which can be re-read)
onto the argument aStream

Usage example(s):

     Dictionary new storeOn:Transcript
     Dictionary new storeString

     (Dictionary new at:1 put:'hello'; yourself) storeOn:Transcript

     (Dictionary new at:1 put:'hello'; at:2 put:nil; yourself) storeOn:Transcript
     
     (Dictionary new at:1 put:'hello'; at:2 put:#(1 2 3 4 5); yourself) storeOn:Transcript
     (Dictionary new at:1 put:'hello'; at:2 put:#(1 2 3 4 5) asSet; yourself) storeOn:Transcript

Usage example(s):

     |d|
     d := Dictionary new.
     d at:1 put:'hello'.
     d at:'hello' put:#world.
     d storeOn:Transcript

private
o  compareSame: element1 with: element2
compare two elements for being the same. Here, return true if the
elements are equal (i.e. using #=).
Redefinable in subclasses.

o  emptyCollectionForKeys
return an empty collection to hold keys. Here, a Set is returned.
Redefinable in subclasses.

o  grow: newSize
grow the receiver to make space for at least newSize elements.
To do this, we have to rehash into the new arrays.
(which is done by re-adding all elements to a new, empty key/value array pair).

o  initializeForCapacity: minSize
initialize the contents array (for at least minSize slots)
and set tally to zero.
The size is increased to the next prime for better hashing behavior.

o  rehash
rehash contents - is done by re-adding all elements to a new, empty key/value array pair).

o  rehashFrom: startIndex
marked as obsolete by Stefan Vogel at 18-Apr-2023

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  shrinkToZero
Dictionary new shrinkToZero capacity

o  valueContainerOfSize: n
return a container for values of size n.
Extracted to make life of weak subclasses easier ...

queries
o  hasLiteralContents
answer true, if all my elements are literals

o  includes: anObject
return true, if the argument, aValue is stored in the dictionary,
i.e. if there is an associaten, with aValue as value.
This is a slow search, since there is no fast reverse mapping;
the values have to be all scanned without any hashing.
You need a special collection (or two Dictionaries) to get this
reverse mapping fast.

o  includesAssociation: anAssociation
return true, if there is an association in the receiver with the
same key and value as the argument, anAssociation.
NOTICE: in contrast to #includes:, this compares both key and value.

o  includesEqualValue: aValue
return true, if the argument, aValue is stored in the dictionary,
i.e. if there is an associaten, with aValue as value.
This is a slow search, since there is no fast reverse mapping;
the values have to be all scanned without any hashing.
You need a special collection (or two Dictionaries) to get this
reverse mapping fast.

o  includesIdenticalKey: aKey
return true, if the argument, aKey is a key in the receiver

Usage example(s):

        (Dictionary withKeysAndValues:#('keyString' 1 keySymbol 2))
            includesIdenticalKey:'keyString'.

        (Dictionary withKeysAndValues:#('keyString' 1 keySymbol 2))
            includesIdenticalKey:#keySymbol.

o  includesIdenticalValue: aValue
return true, if the argument, aValue is stored in the dictionary,
i.e. if there is an associaten, with aValue as value.
This is a slow search, since there is no fast reverse mapping;
the values have to be all scanned without any hashing.
You need a special collection (or two Dictionaries) to get this
reverse mapping fast.

o  includesKey: aKey
return true, if the argument, aKey is a key in the receiver

o  includesValue: aValue
return true, if the argument, aValue is stored in the dictionary,
i.e. if there is an associaten, with aValue as value.
This is a slow search, since there is no fast reverse mapping;
the values have to be all scanned without any hashing.
You need a special collection (or two Dictionaries) to get this
reverse mapping fast.

o  occurrencesOf: anObject
count & return how often anObject is stored in the dictionary.
This counts values - not keys. Uses #= (i.e. equality) compare.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'1' put:'one'.
     d occurrencesOf:nil.

Usage example(s):

     |d|

     d := Dictionary new.
     d at:'1' put:'one'.
     d at:2 put:nil.
     d at:5 put:nil.
     d occurrencesOf:nil.

o  speciesForCollecting
like species, but used when doing collect operations.
Redefined for collections which return a different classes object when doing collect.

searching
o  findFirst: aBlock ifNone: exceptionValue
find the index of the first element, for which evaluation of the argument, aBlock returns true;
return its index or the value from exceptionValue if none detected.
This is much like #detect:ifNone:, however, here an INDEX is returned,
while #detect:ifNone: returns the element.

Here we return the first key for which aBlock matches the value.
Note that there is no order in a Dictionary, so any element is first.

Usage example(s):

        (Dictionary withKeys:#('a' 'b' 'c') andValues:#('bla' 'hello' 'hallo'))
            findFirst:[:v| v first = $h].

o  findFirstKey: aBlock
find and return the first key, for which evaluation of the argument, aBlock
returns true; return nil if none is detected.

testing
o  isDictionary
return true, if the receiver is some kind of dictionary;
true returned here - the method is redefined from Object.

visiting
o  acceptVisitor: aVisitor with: aParameter
dispatch for visitor pattern; send #visitDictionary:with: to aVisitor

xml conversion
o  asXMLDocument
( an extension from the stx:goodies/xml/stx package )

o  asXMLElementNamed: aName
( an extension from the stx:goodies/xml/stx package )


Examples:


    |d|

    d := Dictionary new.
    d at:'1' put:'one'.
    d at:2 put:'two'.
    d at:2
    |d|

    d := Dictionary new.
    d at:'1' put:'one'.
    d at:2   put:nil.
    d.
    d at:2
    |d|

    d := Dictionary new.
    d at:'1' put:'one'.
    d at:2   put:nil.
    d includes:nil.
    |d|

    d := Dictionary new.
    d at:'1' put:'one'.
    d includes:nil.
    |d1 d2|

    d1 := Dictionary withKeys:#(a b c) andValues:#( 1 2 3).
    d2 := Dictionary newFrom:d1.
    d2.


ST/X 7.7.0.0; WebServer 1.702 at 20f6060372b9.unknown:8081; Mon, 18 Nov 2024 00:29:48 GMT