HashMap Inputs/Meanings

Hi All,

I was just wondering what the input parameters for HashMap are/mean? Also, in a HashMap, can only one thing be linked to another by an order of 1? e.g. 1x UserId to 1x Profile? Is it possible to do several UserIds to one Guild? Or is this not necessary?

Many thanks,
Kev

1 Like

Something like this…

import HashMap "mo:base/HashMap";
import Hash "mo:base/Hash";
import Prim "mo:prim";
import Option "mo:base/Option";

func hashMapTest() : Nat {
    let eq:      (Nat,Nat)->Bool  = func(x,y) { x==y };
    let keyHash: (Nat)->Hash.Hash = func(x)   { Prim.natToWord32 x }; // type Hash is a Word32

    let store = HashMap.HashMap<Nat,Nat>(8, eq, keyHash);

    ignore store.set(1, 10);
    ignore store.set(2, 10);
    ignore store.set(3, 20);

    Option.unwrap<Nat>(store.get(2));   // returns 10        
};

The eq function is a predicate to define the search criteria, and the result of keyHash will be used to make an initial fast lookup, before checking the actual key matches. You could also consider using a Trie, which makes use of similar functions to these.

You can have many keys map to the same value (as with 1 and 2 above), so multiple UserIds (keys) could hold the same Guild (value) yes.

Also you should handle the optional value store.get() returns, use a switch statement, in case the key you’re looking up doesn’t exist. I can add this if you like.

3 Likes

Hi Ori,

Thanks for the swift reply! Please may you add that final example you suggested also? And what is the ‘8’ in (8, eq, keyHash) ?. Can a person be a member of multiple Guilds? e.g can the multiple mapping be both ways? Lastly, how would a Trie work in a situation with multiple people in a Guild? Would it be more intuitive and syntactically friendly?

1 Like

Of course:

func hashMapTest() : Nat {
    let eq:      (Nat,Nat)->Bool  = func(x,y) { x==y };
    let keyHash: (Nat)->Hash.Hash = func(x)   { Prim.natToWord32 x }; // type Hash is a Word32

    let store = HashMap.HashMap<Nat,Nat>(8, eq, keyHash);

    ignore store.set(1, 10);
    ignore store.set(2, 10);
    ignore store.set(3, 20);

    let storedValue = store.get(2);

    switch (storedValue) {
        case (?value) { 
            return value;               // returns 10
        };
        case (null) {
            // handle not finding a value in the store, for example:
            return 0;                   // returns 0
        };
    };
};

The 8 is the initial capacity of the HashMap, I just chose a number arbitrarily. The size of the hash table will grow when it gets close to filling up, it’s quite an expensive operation so you might want to start higher. Currently this just happens each time the ceiling is hit, in HashMap.mo in the base library files:

Internally, table growth policy is very simple, for now:
Double [the] capacity when the expected bucket list [is] beyond a certain constant.

HashMap is likely a good fit so you can stick with that, but tries can be useful in some situations, it’s worth exploring a bit on Wikipedia and the like. If you want to use one then you could try Motoko’s TrieMap, it has the same interface as HashMap so you can use it in the same way.

You can have a person entry hold multiple guilds (it could point to a list of guilds for example), it’s a bit of a larger design question, as per your other threads; some in here might have ideas on various ways to approach this?

1 Like

Thanks for the reply Ori! Very informative. It seems like HashMap is a good choice for assigning an ID to a profile and maybe a few IDs to a Guild, but not necessarily assigning one ID to several Guilds, which is fine, as you mentioned, we’ve queried this before. I wondered if it was a potential solution, but maybe not in this case. Hopefully we can get some suggestions!

3 Likes

It’s good for all of those really, it’s just about which way around you want things. At this level you’re mostly thinking key-value store, maybe not specifically a hashmap, ie do I want lookups by key? Or do I actually need to iterate through a list of them, or index in by position etc. ? If you really need all these things then you can start thinking about creating extra data structures to accommodate them. But if you explore it enough in design you might find that some of them aren’t actually needed, or you can get away without them. Get the whiteboard and pens out, you’re on the right track with your thinking in the other threads ; )

2 Likes

Thanks again, Ori, very interesting food for thought, looks like I’m going to have to think hard about this one!

1 Like