Iterable set
Maps can't be enumerated. To list members, keep a list and an index map in sync. This is the pattern behind the generic SetEnumerable module.
iterable-set.lithe
(* Maps cannot be iterated. When you need to list members, keep an enumerable set:
a list for order plus an index map for membership, updated together. Removal
swaps the last member into the gap, so it costs the same for any member. *)
import "std/evm";
error OutOfRange of uint;
storage is record {
members : list(address);
count : uint;
(* index + 1, so that 0 means absent *)
positions : map(address, uint);
}
external view function contains(const who : address) : bool is
storage.positions[who] > 0
external function join() is {
if storage.positions[Evm.sender()] = 0 then {
storage.members[storage.count] := Evm.sender();
storage.count := storage.count + 1;
storage.positions[Evm.sender()] := storage.count;
}
}
external function leave() is {
const position : uint := storage.positions[Evm.sender()];
if position > 0 then {
const last : address := storage.members[storage.count - 1];
storage.members[position - 1] := last;
storage.positions[last] := position;
storage.positions[Evm.sender()] := 0;
storage.count := storage.count - 1;
}
}
external view function length() : uint is
storage.count
(* Clients page through members one index at a time. *)
external view function at(const i : uint) : address
raises OutOfRange
is {
assert(i < storage.count, OutOfRange(i));
} with storage.members[i]joinandleavecost the same no matter how many members there are.- Off-chain clients enumerate with
length()andat(i).

