Lists, sets, and maps
Lists
A list of elements of type t
has type sp.list[t]
. For example, the list [1, 2, 3]
has type sp.list[sp.int]
.
SmartPy lists have different methods than Python lists. For example, to add an element to a SmartPy list, use its push()
method, not the standard Python append()
method.
Here is an example of creating and using a SmartPy list:
import smartpy as sp
@sp.module
def main():
class ListTest(sp.Contract):
def __init__(self):
self.data.myList = []
sp.cast(self.data.myList, sp.list[sp.int])
@sp.entrypoint
def add(self, newValue):
self.data.myList.push(newValue)
@sp.add_test()
def test():
scenario = sp.test_scenario("ListTest", main)
contract = main.ListTest()
scenario += contract
contract.add(4)
contract.add(6)
scenario.verify(sp.sum(contract.data.myList) == 10)
- sp.len(x: sp.list[t]) → sp.nat
Returns the length of a list, for example
sp.len(["a", "b", "c"]) == 3
.
- sp.sum(xs: sp.list[sp.int]) → sp.int
- sp.sum(xs: sp.list[sp.nat]) → sp.nat
Returns the sum of all the elements in a list, for example
sp.sum([1, 2, 3]) == 6
. Works on lists of bothsp.nat
andsp.int
.
- l.push(x: t)
Adds an element to the start of a list. For example:
smartpys = [1, 2, 3] s.push(4) # evaluates to `[4, 1, 2, 3]`
- sp.cons(x: t, x: sp.list[t]) → sp.list[t]
Returns a new list with an element added to the front. For example
sp.cons(1, [2, 3])
evaluates to[1, 2, 3]
.
- sp.range(to: sp.nat) → sp.list[sp.nat]
- sp.range(to: sp.int) → sp.list[sp.int]
- sp.range(from_: sp.nat, to: sp.nat) → sp.list[sp.nat]
- sp.range(from_: sp.int, to: sp.int) → sp.list[sp.int]
- sp.range(from_: sp.nat, to: sp.nat, step: sp.nat) → sp.list[sp.nat]
- sp.range(from_: sp.int, to: sp.int, step: sp.int) → sp.list[sp.int]
Get a series of numbers with the specified parameters.
sp.range(3)
evaluates to[0, 1, 2]
.sp.range(3, 7)
evaluates to[3, 4, 5, 6]
.sp.range(3, 7, 2)
evaluates to[3, 5]
.
- reversed(x: sp.list[t]) → sp.list[t]
Reverse and return a new list from the provided list. For example,
reversed([1, 2, 3])
evaluates to[3, 2, 1]
.
Sets
A set containing elements of type t
is represented as {...}
and has the type sp.set[t]
. For instance, the set {1, 2, 3}
is of type sp.set[sp.int]
. To create an empty set, use set()
.
SmartPy sets have different methods than Python sets. For example, to check if an element is in a SmartPy set, use its contains()
method, not the standard Python in
operator.
Here is an example of creating and using a SmartPy set:
import smartpy as sp
@sp.module
def main():
class SetTest(sp.Contract):
def __init__(self):
self.data.mySet = set()
sp.cast(self.data.mySet, sp.set[sp.string])
@sp.entrypoint
def add(self, newValue):
self.data.mySet.add(newValue)
@sp.add_test()
def test():
scenario = sp.test_scenario("setTest", main)
contract = main.SetTest()
scenario += contract
contract.add("C")
contract.add("D")
scenario.verify(contract.data.mySet.contains("D"))
Maps
A map that takes elements of type k
to elements of type v
has type sp.map[k, v]
. SmartPy maps are similar to Python's dictionaries.
You can access entries in a map with the m[...]
notation, as in this example:
m = {"a": 65, "b": 66}
assert m["a"] == 65
m["c"] = 67
assert m["c"] == 67
- del m[key: t]
An entry can be deleted from a map using the statement
del m[key]
. For example:smartpym = {"a": 65, "b": 66} del m["a"] assert not m.contains("a")
- m.get(key: k, default=...: v) → v
- m.get(key: t, error=...: t) → v
Looks up
key
in the mapm
. Ifkey
does not have an entry in the map,default
is returned orerror
is raised, according to which keyword argument is given. You must provide eitherdefault
orerror
but not both.
- m.get_opt('key: k') → sp.option[t]
Returns
sp.Some(value)
if the value is found in the mapm
,None
otherwise.
- m.items() → sp.list[sp.record(key=k, value=v)]
{'a': 97, 'b': 98}.items()
evaluates to[sp.record(key='a', value=97), sp.record(key='b', value=98)]
.
- sp.update_map(key: k, value: sp.option[v], m: map[k, v]) → map[k, v]
Returns a copy of
m
with a modified entry atkey
: ifvalue == None
, it is removed; ifvalue == sp.Some(v)
, it isv
.Example:
smartpyassert sp.update_map("a", sp.Some(3), {})["a"] == 3 assert sp.update_map("a", None, {"a": 2}).get("a", default=-1) == -1
- sp.get_and_update(key: k, value: sp.option[v], m: map[k, v]) → sp.pair[sp.option[v], map[k, v]]
Like
sp.update_map
, but also returns the old value of the map (orNone
ifk
had no entry).
Big maps
The big map type (sp.big_map[k, v]
) is a key-value store similar to sp.map
, but it is optimized so it can contain large amounts of data while requiring low gas fees to read and update. Big maps are lazily deserialized, which means that only the entries manipulated by a contract are deserialized and reserialized, not the entire big map. Individual operations on a big map may have higher gas costs than on a map when the maps are small, but as the maps get larger, big maps are often cheaper overall to use. Also, unlike maps, big maps store keys as hashes, which can save space.
Here is an example of creating and working with a big map:
m = sp.big_map()
m["a"] = 65
m["b"] = 66
assert m["a"] == 65
m["c"] = 67
assert m["c"] == 67
Contracts read big maps not as a single piece of data but as a collection of entries. Therefore, contracts cannot read an entire big map at one time, which prevents them from passing big maps as parameters, iterating through entries, or counting the number of entries.
Otherwise, in SmartPy, big maps behave much like maps:
- You can get, add, or update an element in a big map with the
m[...]
notation just lke maps. - You can use
del
to remove an element from a big map. - Big maps have some of the same methods as maps, including
get()
andget_opt()
. - You can use
sp.update_map
andsp.get_and_update
to update big maps.
However, they have these differences:
- To create a big map, you must use the
sp.big_map
function instead of using a literal value. - Big maps do not have these methods that maps have:
items()
keys()
values()
- You cannot iterate over a big map or put one inside another big map.
- You cannot get the length of a big map with
sp.len()
.