Type error [M0082], expected iterable type, but expression has type [[(Principal, PlayerHistoricalStats/1)]]

Hey guys, I have some problems regarding looping through and changing more complex objects in stable memory.

I have an Array / List / HashMap / Iter (I’m horrible with types, sorry) in stable memory that I’d like to loop through and change each element. I managed to figure out how to change one separately (code below). But I can’t seem to understand how types work and the compiler just keeps complaining about something no matter what I try.

Relevant Code Snippets

This is what gets appeneded to the previous array each week

private stable var pastWeeksPlayerScores : [[(Principal, PlayerHistoricalStats)]] = [];

This is an Array to which the following is appended every week

private var weekPlayerScores : HashMap.HashMap<Principal, PlayerHistoricalStats> =
    HashMap.fromIter(_weekPlayerScores.vals(), 0, Principal.equal, Principal.hash);

What I’m trying to do is loop through pastWeeksPlayerScores and for each do what is shown below, basically resetting certain parts of the object to 0s. And I need to loop through pastWeeksPlayerScores and do this since each of those is a list / array / hashmap)

allTimePlayerScores := HashMap.map<Principal, PlayerHistoricalStats, PlayerHistoricalStats>(allTimePlayerScores, Principal.equal, Principal.hash, func (k, v) {
    let updatedScores : PlayerHistoricalStats = {
        arenaStats = v.arenaStats;
        tournamentStats = {
            wins : Nat = 0;
            losses : Nat = 0;
            points : Nat = 0};
        botStats = v.botStats;
     };

     Debug.print("[After]: " # debug_show(updatedScores));
     return updatedScores;
});

I hope this makes sense.

Types mentioned above

public type HistoricalStats = {
    wins: Nat;
    losses: Nat;
    points: Nat;
};

public type PlayerHistoricalStats = {
    arenaStats: HistoricalStats;
    tournamentStats: HistoricalStats;
    botStats: HistoricalStats;
};

In a desperate attempt I tried doing this HashMap.map resetting using pastWeeksPlayerScores[0] to be able to modify elements one by one, but that didn’t work either.

Any help is greatly appreciated!

What errors are you getting?

type error [M0072], field entries does not exist in type [(Principal, PlayerHistoricalStats/1)]

And sometimes if I try something different I get something about not being able to reassign an immutable object. I’ll try to get that exact error tomorrow morning

That error doesn’t seem related to the code above.

When I run:

var updatedScores = HashMap.map<Principal, PlayerHistoricalStats, PlayerHistoricalStats>(pastWeeksPlayerScores[0], Principal.equal, Principal.hash, func (k, v) {
    Debug.print("[Before]: " # debug_show(v));

    return v;
});

like I did before, but trying to pick and modify only the first element pastWeeksPlayerScores[0] I get

type error [M0096], expression of type
  [(Principal, PlayerHistoricalStats/1)]
cannot produce expected type
  {
    delete : Principal -> ();
    entries : () -> Iter/1<(Principal, PlayerHistoricalStats/1)>;
    get : Principal -> ?PlayerHistoricalStats/1;
    put : (Principal, PlayerHistoricalStats/1) -> ();
    remove : Principal -> ?PlayerHistoricalStats/1;
    replace :
      (Principal, PlayerHistoricalStats/1) -> ?PlayerHistoricalStats/1;
    size : () -> Nat
  }

And of course now the previous one makes sense too. I must have tried to do debug_show(v.entries()) instead of debug_show(v)

I think I’m not specifying the return type correctly somewhere. Because v in debug_show(v) is actually the object I want to modify

I feel like this should work but it gives the same error.

pastWeeksPlayerScores := Array.tabulate<[(Principal, PlayerHistoricalStats)]>(Constants.WEEKS_HISTORY_COUNT, func(i: Nat) {
    let updatedScores = HashMap.map<Principal, PlayerHistoricalStats, PlayerHistoricalStats>(pastWeeksPlayerScores[i], Principal.equal, Principal.hash, func (k, v) {
        return v;
    });
});

Basically loops through takes each array of objects and returns the same values for all keys. Basically replacing everything with itself. It’s the same error as before:

type error [M0096], expression of type
  [(Principal, PlayerHistoricalStats/1)]
cannot produce expected type
  {
    delete : Principal -> ();
    entries : () -> Iter/1<(Principal, PlayerHistoricalStats/1)>;
    get : Principal -> ?PlayerHistoricalStats/1;
    put : (Principal, PlayerHistoricalStats/1) -> ();
    remove : Principal -> ?PlayerHistoricalStats/1;
    replace :
      (Principal, PlayerHistoricalStats/1) -> ?PlayerHistoricalStats/1;
    size : () -> Nat
  }

I’m a bit confused by what you’re trying to do here. What is the type of pastWeeksPlayerScores?

2 things that I don’t understand are:

  1. Why you’re providing a list of tuples. Did you mean to provide HashMap.fromIter(pastWeeksPlayerScores)?

    The first argument to HashMap.map should be a HashMap. It looks like you’re providing the first element in a list of tuples.

  2. Why you’re trying to index into the list. HashMap.map will call the function you provide on each key-value pair in the hash map.

I’m also confused, so I’m not surprised that my confusion is contagious. I inherited a codebase that works really well, however now my task is to reset some values in a stable variable, but not all.

There is a variable called weekPlayerScores which is a HashMap and defined as

private stable var _weekPlayerScores : [(Principal, PlayerHistoricalStats)] = [];

private var weekPlayerScores : HashMap.HashMap<Principal, PlayerHistoricalStats> =
        HashMap.fromIter(_weekPlayerScores.vals(), 0, Principal.equal, Principal.hash);

After each week, this weekPlayerScores is appended to a list to store every week in an array like so

pastWeeksPlayerScores := Array.append(pastWeeksPlayerScores, [Iter.toArray(weekPlayerScores.entries())]);

I can modify each of these weekPlayerScores HashMaps separately (only for the current week). So my monkey brain thought I would be able to loop through this pastWeeksPlayerScores array and modify each HashMap separately.

I’ll try out what you wrote in 1 in a second. But is this explanation a bit clearer than before? Apologies for not explaining it well enough before. And really appreciate you spending time on this!

Now that I read you answer many times:

  1. Yes! I’m trying to provide a HasMap as the first argument. Since I believe the pastWeeksPlayerScores is an Array of HashMaps. And don’t know how to check that. In my answer above I laid out how this Array gets HashMaps appended to it. So logically:
ArrayOfHashMaps[0] -> HashMap
  1. I want to index into this list / Array to get and modify each HashMap separately. First I’d just index into it to get something working, later this should be a for loop.

This is reasonable, but depending on how pastWeeksPlayerScores is declared you may be running into the same problem trying to mutate a list that you faced when attempting to mutate a hash map.

I would try something like this:

pastWeeksPlayerScores := List.map(func (weekPlayerScores) {
  HashMap.map(weekPlayerScores, _, _, func (k, v) {
      // modify your value here
    });
});

As before, you’ll probably need to provide some type arguments or annotations to make the above type check.

Thanks again for the answer! I tried it out but I just can’t match types no matter what I’m trying. I thing the root problem is that the error messages are telling me exactly what is wrong, however my Motoko type understanding is just lacking. For example I often get this:

type error [M0075], expected array type, but expression produces type
  {
    delete : Principal -> ();
    entries : () -> Iter/1<(Principal, PlayerHistoricalStats/1)>;
    get : Principal -> ?PlayerHistoricalStats/1;
    put : (Principal, PlayerHistoricalStats/1) -> ();
    remove : Principal -> ?PlayerHistoricalStats/1;
    replace :
      (Principal, PlayerHistoricalStats/1) -> ?PlayerHistoricalStats/1;
    size : () -> Nat
  }

Is this an object type? That has all these seemingly predefined methods like remove, replace and delete?

Edit 1:
Also, if I returned (vec {}) does that mean an empty Array, or can this hold something that I can access?

Edit 2:
And what’s the problem here?

type error [M0096], expression of type
  {next : () -> ?(Principal, PlayerHistoricalStats/1)}
cannot produce expected type
  (Principal, PlayerHistoricalStats/1)

or:

type error [M0096], expression of type
  ?(Principal, PlayerHistoricalStats/1)
cannot produce expected type
  (Principal, PlayerHistoricalStats/1)

God, if I ever understand this, I’ll make a tutorial series on Motoko types.

If you can point me to a GitHub repo or more complete code dump with the latest code I’ll be in a much better position to understand where these error messages are coming from.