Skip to content

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:

smartpy
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 both sp.nat and sp.int.

l.push(x: t) → 

Adds an element to the start of a list. For example:

smartpy
s = [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].

for x in  l → 

Iterate over a list. For example:

smartpy
for x in [1, 2, 3]:
    self.data.result += x

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:

smartpy
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"))
sp.len(x: sp.set[t]) → sp.nat

Returns the number of elements in a set.

s.contains(x: t) → sp.bool

Returns a boolean value indicating whether x is an element of the set.

s.elements() → sp.list[t]

Returns the elements of a set as a list.

s.add(x: t) → 

Adds an element to a set. For example:

smartpy
s = {1, 2, 3}
s.add(4)
assert s.contains(4)

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:

smartpy
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:

smartpy
m = {"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 map m. If key does not have an entry in the map, default is returned or error is raised, according to which keyword argument is given. You must provide either default or error but not both.

m.get_opt('key: k') → sp.option[t]

Returns sp.Some(value) if the value is found in the map m, None otherwise.

sp.len(x: sp.map[k, v]) → sp.nat

Returns the size of a map, which is the number of its entries.

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)].

m.keys() → sp.list[k]

{'a': 97, 'b': 98}.keys() evaluates to ['a', 'b'].

m.values() → sp.list[k]

{'a': 97, 'b': 98}.values() evaluates to [97, 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 at key: if value == None, it is removed; if value == sp.Some(v), it is v.

Example:

smartpy
assert 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 (or None if k 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:

smartpy
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() and get_opt().
  • You can use sp.update_map and sp.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().