Hello! Just trying out Motoko for the first time, and I’m a little stuck on how to implement a HashMap properly as a key/value store. I tried to use an example from a different HashMap post in this forum but I don’t think it’s quite applicable to my use case. I’d like to index all Topics I’m trying to persist by their “primary key”, a Nat, like so:
import H "mo:base/HashMap";
import Hash "mo:base/Hash";
import Prim "mo:prim";
import types "types";
import utils "utils";
type Topic = types.Topic;
actor Board {
let eq: (Nat,Nat) ->Bool = func(x, y) { x == y };
let keyHash: (Nat) -> Hash.Hash = func(x) { Prim.natToWord32 x }
var topics = H.HashMap<Nat, Topic>(8, eq, keyHash);
var nextId : Nat = 1;
...
public func addTopic (title : Text, description : Text) : async () {
utils.add(topics, nextId, title, description);
nextId += 1;
topics
};
};
I think I’m just not quite grokking how to use the type system properly or what interface I need to implement on Topic or otherwise to make this work… Thanks in advance!
Welcome! Just to start, returning topics from addTopic there is probably (hopefully) giving a compiler error? So you can remove that line. You likely don’t need topics to be var either.
If this is still giving you issues, maybe try just using topics.put() directly in the addTopic method for now and go from there…
Also you can just replace keyHash with the Hash module’s Hash.hash function too since it has the same signature, I was being a bit explicit in the example you found. So:
let topics = H.HashMap<Nat, Topic>(8, eq, Hash.hash);
Thanks for this! I will give it a try and see how it works
Hah yeah on returning topics out of that function, you’re definitely right that I have the wrong output signature I’m sure that compiler error would have popped up if it hadn’t triggered sooner on my construction of the HashMap–should have vetted my example code a little more before posting
Any public methods in this actor can be called externally by other canisters and you can’t pass mutable types between canisters. The topics HashMap works with mutable types internally.
If you want to return all topics then you could have getTopics create an array of type [Topic] by iterating through the hashmap (HashMap has an entries() method for this, eg. for (entry in topics.entries())... ) and return this array instead.
Ah! This makes a lot of sense! I knew I was missing something, just didn’t realize it was the programming paradigm, not just the type system Thanks for taking the time to explain
FWIW I was trying to go down the road of returning a key/value structure to my FE canister b/c with a large dataset that would be better than looping over an array to find a specific record…is there a better, immutable key/value-like object that would fit this use case? At this point totally theoretical, just looking to better understand what good patterns look like in this programming paradigm
No prob. You could take a look at Paul Liu’s reversi game https://github.com/ninegua/reversi and see how he uses types to create immutable views on the data, then freezes his data structures to them for returning. This is a good approach for more complex types; it would inherently use iteration again but this is de rigour really.
Thanks! Yeah I think I was trying to be too fancy–good to know that just standard iteration over an array or similar pattern is the best practice. I’m all in on convention and consistency, so iteration it is