# How to maintain nested hashmaps as stable

**URL:** <https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634>\
**Category:** Language Support\
**Tags:** Motoko\
**Created:** [August 22, 2021, 5:15am UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634 "2021-08-22T05:15:24Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![imponenm](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/imponenm/32/2140_2.png) [@imponenm](https://forum.dfinity.org/u/imponenm)\
**Post date:** [August 22, 2021, 5:15am UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/1 "2021-08-22T05:15:24Z")

</div>

Let’s say I create hashmap where every key is assigned to another hashmap, and I populate this with some values:

```auto
var x1 = H.HashMap<Text, Nat>(2, Text.equal, Text.hash);
var x2 = H.HashMap<Text, Nat>(2, Text.equal, Text.hash);
var map = H.HashMap<Text, H.HashMap<Text, Nat>>(10, Text.equal, Text.hash);
        
x1.put("a1", 50);
x2.put("a1", 100);
map.put("2021-09-22", x1);
map.put("2021-09-23", x2);

```

How do I go about maintaining the state of this variable? I understand that I’m not able to declare the map variable as stable, and I’ve taken a look at [pre-upgrade and post-upgrade](https://sdk.dfinity.org/docs/language-guide/upgrades.html#_declaring_stable_variables) functions, but I’m having trouble converting this hashmap into the correct kind of stable list.

So far I’ve tried iterating through that map variable in an attempt to write it to a list of type [(Text, [ (Text, Nat)] ) ], but I haven’t had any success so far. I’m not used to declaring list sizes in advance, so this is giving me some trouble. Is there an easy way to do this, or is there a different way I should be thinking about this?

---

<div class="post-metadata">

**Author:** ![peterparker](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@peterparker](https://forum.dfinity.org/u/peterparker)\
**Post date:** [August 22, 2021, 9:00am UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/2 "2021-08-22T09:00:04Z")

</div>

You can create a stable array with the same types as your hashmaps and indeed use pre-post upgrade.

For example this [playground](https://m7sm4-2iaaa-aaaab-qabra-cai.raw.ic0.app/?tag=4031577968)

```auto
private var canisters: HashMap.HashMap<Principal, CanisterId> = HashMap.HashMap<Principal, CanisterId>(10, isPrincipalEqual, Principal.hash);

private stable var upgradeCanisters : [(Principal, CanisterId)] = [];

system func preupgrade() {
        upgradeCanisters := Iter.toArray(canisters.entries());
    };

    system func postupgrade() {
        canisters := HashMap.fromIter<Principal, CanisterId>(upgradeCanisters.vals(), 10, isPrincipalEqual, Principal.hash);
        upgradeCanisters := [];
    };

```

---

<div class="post-metadata">

**Author:** ![peterparker](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@peterparker](https://forum.dfinity.org/u/peterparker)\
**Post date:** [August 22, 2021, 9:02am UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/3 "2021-08-22T09:02:15Z")

</div>

On `preupgrade` you call `Iter.toArray` to transform / populate the HashMap to the stable Array.

On `postupgrade` you call the `HashMap.fromIter` to transform / populate the stable Array to HashMap.

---

<div class="post-metadata">

**Author:** ![imponenm](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/imponenm/32/2140_2.png) [@imponenm](https://forum.dfinity.org/u/imponenm)\
**Post date:** [August 22, 2021, 6:08pm UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/4 "2021-08-22T18:08:52Z")

</div>

Hi @peterparker, I’ve tried something similar, but it doesn’t work for the code sample I’ve posted above because the hashmap I’m converting to an array contains another hashmap. Going off the initial example I posted:

```auto
var map = H.HashMap<Text, H.HashMap<Text, Nat>>(10, Text.equal, Text.hash);
stable var upgradeMap : [(Text, H.HashMap<Text, Nat>)] = [];
// ...
system func preupgrade() {
    upgradeMap := Iter.toArray(map.entries());
};

```

Would generate an error:

```auto
main.mo:21.16-21.28: type error [M0131], variable upgradeDates is declared stable but has non-stable type
  [(Text, HashMap<Text, Nat>)]

```

I have also tried defining upgradeMap as follows:

`stable var upgradeMap : [(Text, [(Text, Nat)])] = [];`

But this still produces an error:

```auto
main.mo:38.23-38.50: type error [M0098], cannot implicitly instantiate function of type
  <A>(Iter/1<A>) -> [A]
to argument of type
  Iter/1<(Text, HashMap<Text, Nat>)>
to produce result of type
  [(Text, [(Text, Nat)])]
because implicit instantiation of type parameter A is over-constrained with
  (Text, HashMap<Text, Nat>) <: A <: (Text, [(Text, Nat)])
where
  (Text, HashMap<Text, Nat>) </: (Text, [(Text, Nat)])
so that no valid instantiation exists

```

I’m pretty sure I understand why the errors are occurring, I’m just not familiar with the proper syntax to apply Iter.toArray() in scenario like this, where I have a hashmap inside a hashmap

---

<div class="post-metadata">

**Author:** ![peterparker](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@peterparker](https://forum.dfinity.org/u/peterparker)\
**Post date:** [August 22, 2021, 6:23pm UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/5 "2021-08-22T18:23:11Z")

</div>

> [@imponenm](#):
>
> the hashmap I’m converting to an array contains another hashmap.

Actually, I also have got such an example in my [repo](https://github.com/deckgo/deckdeckgo/blob/feat/internet-computer/studio/canisters/decks/decks.store.mo) 😉. Not sure that the best solution but, it works out for me.

From my actor class functions pre/postupgrade I call another class that contains following functions:

```auto
public func preupgrade(): HashMap.HashMap<UserId, [(DeckId, OwnerDeckBucket)]> {
            let entries : HashMap.HashMap<UserId, [(DeckId, OwnerDeckBucket)]> = HashMap.HashMap<UserId, [(DeckId, OwnerDeckBucket)]>(10, Utils.isPrincipalEqual, Principal.hash);

            for ((key: UserId, value: HashMap.HashMap<DeckId, OwnerDeckBucket>) in decks.entries()) {
                let ownerDecks : [(DeckId, OwnerDeckBucket)] = Iter.toArray<(DeckId, OwnerDeckBucket)>(value.entries());
                entries.put(key, ownerDecks);
            };

            return entries;
        };

        public func postupgrade(entries: [(UserId, [(DeckId, OwnerDeckBucket)])]) {
            for ((key: UserId, value: [(DeckId, OwnerDeckBucket)]) in entries.vals()) {
                let ownerDecks: HashMap.HashMap<DeckId, OwnerDeckBucket> = HashMap.fromIter<DeckId, OwnerDeckBucket>(Iter.fromArray<(DeckId, OwnerDeckBucket)>(value), 10, Text.equal, Text.hash);

                decks.put(key, ownerDecks);
            };
        };

```

My idea is to convert first the `HashMap<Something, Hashmap<Something, Something>>` to `HashMap<Something, [(Something, Something)])>` before converting it to the stable array on preupgrade and the reverse operation on postupgrade.

Downside of the approach is that the upgrade might become slow over time as I iterate over the hashmap on upgrades to convert each sub-hashmaps to arrays but, like I said, does the job.

Let me know if that would work for you?

---

<div class="post-metadata">

**Author:** ![imponenm](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/imponenm/32/2140_2.png) [@imponenm](https://forum.dfinity.org/u/imponenm)\
**Post date:** [August 22, 2021, 6:49pm UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/6 "2021-08-22T18:49:17Z")

</div>

This is exactly what I was looking for, thank you! I was trying to do the same thing, but I’m still getting used to the Motoko syntax. This was very helpful

---

<div class="post-metadata">

**Author:** ![peterparker](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@peterparker](https://forum.dfinity.org/u/peterparker)\
**Post date:** [August 22, 2021, 6:56pm UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/7 "2021-08-22T18:56:08Z")

</div>

> [@imponenm](#):
>
> I’m still getting used to the Motoko syntax

Me too!

Happy to hear it helped and let me know if it works out.

---

<div class="post-metadata">

**Author:** ![imponenm](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/imponenm/32/2140_2.png) [@imponenm](https://forum.dfinity.org/u/imponenm)\
**Post date:** [August 22, 2021, 7:24pm UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/8 "2021-08-22T19:24:39Z")

</div>

> [@peterparker](#):
>
> `HashMap.HashMap<UserId, [(DeckId, OwnerDeckBucket)]>`

It did wind up working out using similar syntax as your example. I can see this being slow if the hashmap gets too large, but for now, I don’t expect to upgrading too frequently

---

<div class="post-metadata">

**Author:** ![peterparker](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@peterparker](https://forum.dfinity.org/u/peterparker)\
**Post date:** [August 22, 2021, 7:27pm UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/9 "2021-08-22T19:27:51Z")

</div>

Cool thanks for the feedback

---

<div class="post-metadata">

**Author:** ![rossberg](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/rossberg/32/795_2.png) [@rossberg](https://forum.dfinity.org/u/rossberg)\
**Post date:** [August 23, 2021, 7:36am UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/10 "2021-08-23T07:36:42Z")

</div>

I would recommend first converting the outer hashmap to an array and then map over that. That way, you only allocate one intermediate array instead of an intermediate hashmap, which is more expensive.

You can also do it in one pass, by creating and initialising the target array manually instead of using toArray.

---

<div class="post-metadata">

**Author:** ![peterparker](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@peterparker](https://forum.dfinity.org/u/peterparker)\
**Post date:** [August 23, 2021, 8:50am UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/11 "2021-08-23T08:50:58Z")

</div>

Sounds super interesting! Don’t you happen to have some sample code to display?

---

<div class="post-metadata">

**Author:** ![rossberg](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/rossberg/32/795_2.png) [@rossberg](https://forum.dfinity.org/u/rossberg)\
**Post date:** [August 23, 2021, 9:22am UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/12 "2021-08-23T09:22:35Z")

</div>

Something along the lines of (untested):

```auto
var map = H.HashMap<Text, H.HashMap<Text, Nat>>(10, Text.equal, Text.hash);
stable var upgradeMap : [var (Text, [(Text, Nat)])] = [var];

system func preupgrade() {
    upgradeMap := Array.init(map.size(), ("", []));
    var i = 0;
    for ((x, y) in map.entries()) {
      upgradeMap[i] := (x, Iter.toArray(y.entries()));
      i += 1;
    };
};

```

---

<div class="post-metadata">

**Author:** ![peterparker](https://avatars.discourse-cdn.com/v4/letter/p/b9bd4f/32.png) [@peterparker](https://forum.dfinity.org/u/peterparker)\
**Post date:** [August 23, 2021, 1:07pm UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/13 "2021-08-23T13:07:10Z")

</div>

Nice! Thanks for the snippet, gotcha.

---

<div class="post-metadata">

**Author:** ![MCLYang](https://sea1.discourse-cdn.com/flex023/user_avatar/forum.dfinity.org/mclyang/32/3447_2.png) [@MCLYang](https://forum.dfinity.org/u/MCLYang)\
**Post date:** [July 13, 2022, 10:43pm UTC](https://forum.dfinity.org/t/how-to-maintain-nested-hashmaps-as-stable/6634/14 "2022-07-13T22:43:42Z")

</div>

This works for me. I use nested loop and pattern match to approach it

```auto
private stable var currentSellOffersEntries : [(Nat,(Principal,(Price,Time.Time)))] = [];
private type OfferInfo = HashMap.HashMap<Principal,(Price,Time.Time)>;
private var currentSellOffers = HashMap.HashMap<Nat, OfferInfo>(currentSellOffersEntries.size(), Nat.equal, Hash.hash);
system func preupgrade() {
        // upgrade for nested hashmap
        currentSellOffersEntries := [];
        for (currentSellOffer in currentSellOffers.entries()){
            // entry1: (Nat, OfferInfo)
            let tokenId : Nat = currentSellOffer.0;
            let offerInfo: OfferInfo = currentSellOffer.1;
            for (offer in offerInfo.entries()){
                // offer : (Principal,(Price,Time.Time))
                let user : Principal = offer.0;
                let price : Price = offer.1.0;
                let timestamp : Time.Time = offer.1.1;
                currentSellOffersEntries := Array_append(currentSellOffersEntries, [(tokenId,(user,(price,timestamp)))])
            };
        };
};

system func postupgrade() {
        // currentSellOffersEntries : [(Nat,(Principal,(Price,Time.Time)))]
        for (entry in currentSellOffersEntries.vals()){
            // entry: (Nat,(Principal,(Price,Time.Time)))
            let tokenId : Nat = entry.0;
            let user : Principal = entry.1.0;
            let price : Price = entry.1.1.0;
            let timestamp : Time.Time = entry.1.1.1;

            switch (currentBuyOffers.get(tokenId)){
                case (?currentBuyOffer){
                    // offer is a hashmap
                    currentBuyOffer.put(user,(price,timestamp));
                    currentBuyOffers.put(tokenId,currentBuyOffer);
                };
                case (_){
                    let currentBuyOffer: OfferInfo = HashMap.HashMap<Principal,(Price,Time.Time)>(1, Principal.equal, Principal.hash);
                    currentBuyOffer.put(user,(price,timestamp));
                    currentBuyOffers.put(tokenId,currentBuyOffer);
                };
            };
        };
};

```

The key idea here is we must copy the nested hashmap to entries within `preupgrade()` so let our data safely pass the upgrading. When the upgrade completed, the nested hashmap is empty then iterate everything and copy back to hashmap in `postupgrade()` to complete the variables initiation.
