Is there a stable type like a HashMap where the key can be of Principal type and the value a custom type?

Basically seeking something like this:

stable var users : HashMap.HashMap<Principal, UserData> = HashMap.empty();

Where for example

type UserData = {
    principal : Principal;
    favorite_color : Text;
    name : Text;
    favorite_number : Nat;
  };

Not that I have seen.

I know there was a stable b-tree implementation but i havent used it myself

Otherwise what you would do is have a stable variable that was like a array or another stable data structure

Thank you, I ended up using this for now. As far as I can tell, HashMap is a far better structure than Trie to store principals as keys.

My worry are bugs and security vulnerabilities since it seems to be a little known repo, but really the natural structure is HashMap so I’ll use this for now.

1 Like

How about RBTree? (Which should perhaps better be named TreeMap or something like that.)

We use both motoko-hash-map and the migration pattern in the latest version of axon. See: axon/lib.mo at da8601a65fce0d2536971bca562538fa39f1cd53 · icdevs/axon · GitHub for the ledger implementation.

Is it stable?

I see

stable var rb_users = RBTree.RBTree<Principal, UserData>(Principal.compare);

variable rb_users is declared stable but has non-stable type
  RBTree<Principal, UserData>Motoko

Are there major advantages of doing this over StableHashMap?

Likely yes. I haven’t tested it against that library explicitly, but check this thread: ∞ Day - Origyn Motoko Gift 2 - A Better Map

1 Like

I work with something like this:

For the types

/// Users
    public type UserID   = Principal;
    public type Username = Text;
    public type UserData = {
        username : Username;
        banned   : Bool;
    };

The HashMaps

/// We create an array as a stable var, since hashmaps are not stable
private stable var _users : [(UserID, UserData)] = [];

/// Then we create the hashmap and initialize it from the _users array
var users : HashMap.HashMap<UserID, UserData> = HashMap.fromIter(_users.vals(), 0, Principal.equal, Principal.hash);

And to keep them on every update

//State functions
    system func preupgrade() {
        /// Before an update we put all values from the hashmap to the array, so they can be preserved
        _users := Iter.toArray(users.entries());
    };
    system func postupgrade() {
        /// And since the hashmap is initialized with all the values from the stable array, we can reset the array so in the next update values don't duplicate
        _users := [];
    };

I hope this can help with your main problem (:

1 Like

Thank you, I ended up using this on @skilesare’s advice elsewhere, which I believe doesn’t require pre-postupgrade: GitHub - ZhenyaUsenko/motoko-hash-map: Stable hash maps for Motoko

1 Like

This is working well, I added below

let users = HashMap.HashMap<Principal, User>(0, Principal.equal, Principal.hash);
private stable var _users : [(Principal, User)] = ;

//State functions
system func preupgrade() {
/// Before an update we put all values from the hashmap to the array, so they can be preserved
_users := Iter.toArray(users.entries());
};
system func postupgrade() {
/// And since the hashmap is initialized with all the values from the stable array, we can reset the array so in the next update values don’t duplicate
for ( (p, al ) in _users.vals() ) {
users.put(p, al);
};
_users := ;
};

1 Like