Near Learn

Collections: mappings & arrays

mapping and dynamic arrays become explicit types from near_sdk::store — LookupMap, Vector — each with its own storage prefix.

Beginner5 min read3-question check

Solidity gives you mapping and T[] as language built-ins. NEAR gives you collection types from near_sdk::store that you pick deliberately: LookupMap, Vector, IterableMap, UnorderedSet. Each is a thin wrapper over on-chain key/value storage.

A points ledger
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Points {
    mapping(address => uint32) public points;
    address[] public members;

    function join() external {
        if (points[msg.sender] == 0) {
            members.push(msg.sender);
        }
    }

    function award(address who, uint32 amount) external {
        points[who] += amount;
    }

    function memberCount() external view returns (uint256) {
        return members.length;
    }
}
Rust
use near_sdk::{near, env, AccountId};
use near_sdk::store::{LookupMap, Vector};

#[near(contract_state)]
pub struct Points {
    points: LookupMap<AccountId, u32>,
    members: Vector<AccountId>,
}

impl Default for Points {
    fn default() -> Self {
        Self {
            // each collection needs its own unique storage prefix
            points: LookupMap::new(b"p"),
            members: Vector::new(b"m"),
        }
    }
}

#[near]
impl Points {
    pub fn join(&mut self) {
        let who = env::predecessor_account_id();
        if !self.points.contains_key(&who) {
            self.members.push(who.clone());
            self.points.insert(who, 0);
        }
    }

    pub fn award(&mut self, who: AccountId, amount: u32) {
        let cur = self.points.get(&who).copied().unwrap_or(0);
        self.points.insert(who, cur + amount);
    }

    pub fn points_of(&self, who: AccountId) -> u32 {
        self.points.get(&who).copied().unwrap_or(0)
    }

    pub fn member_count(&self) -> u32 {
        self.members.len()
    }
}
Coming from EVM

mapping(k => v) ↔ LookupMap<K, V>; a dynamic array ↔ Vector<T>. There is no default value magic — reads return Option, so you unwrap_or(..) the "unset" case yourself.

Each collection takes a unique storage prefix (b"p", b"m") — the byte key its entries are stored under. Two collections sharing a prefix silently corrupt each other, so keep them distinct.

LookupMap is get/set by key but not iterable — the cheapest option. Need to iterate entries or list keys? Use IterableMap (or UnorderedMap). Paying only for what you enumerate is the tradeoff NEAR makes for predictable storage gas.

Check yourself

3 questions · progress saved in this browser

  1. 1.What is the NEAR equivalent of mapping(address => uint32)?
  2. 2.In Solidity, points[who] returns 0 for a key that was never set. What does LookupMap::get return on NEAR?
  3. 3.Two collections in the same contract are both created with the prefix b"p". What happens?