Nesse artigo iremos abordar como funcionam os mapping
e arrays
no Solidity, bem como, quando utilizá-los em seu contrato inteligente.
Mapping (Mapas)
Os mapas são utilizados para guardar um valor X em uma chave Y.
São definidos com a sintaxe mapping(keyType => valueType)
keyType
- pode ser definido por qualquer tipo de valor interno, bytes, string, address ou qualquer contrato ou tipo de dado disponível no Solidity.
valueType
- pode ser qualquer tipo de valor, podendo ser até mesmo outro mapa ou matriz.
Os mapas não podem ser iteráveis.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract Mapping {// Mapeamento de endereço para uintmapping(address => uint) public myMap;function get(address _addr) public view returns (uint) {// O mapeamento sempre retorna um valor// Se o valor do mapa nunca foi definido,// ele irá retornar o valor padrão para o tipo de variável atribuindoreturn myMap[_addr];}function set(address _addr, uint _i) public {// Atualiza o valor do mapa para o endereço informadomyMap[_addr] = _i;}function remove(address _addr) public {// Deleta o valor existente para o endereço informadodelete myMap[_addr];}}contract NestedMapping {// Mapeamento aninhado (mapeamento de endereço para outro mapeamento)mapping(address => mapping(uint => bool)) public nested;function get(address _addr1, uint _i) public view returns (bool) {// Você pode obter valores de um mapeamento aninhado// mesmo quando não é inicializadoreturn nested[_addr1][_i];}function set(address _addr1,uint _i,bool _boo) public {nested[_addr1][_i] = _boo;}function remove(address _addr1, uint _i) public {delete nested[_addr1][_i];}}
Uma das aplicações onde o mapping
pode ser utilizado, por exemplo, em um jogo onde você precisa armazenar a pontuação de um determinado jogador
Vamos ver como ficaria esse tipo de aplicação
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract GameScore {// Definimos nosso mapa como playerScores que irá// receber o endereço do jogador e a pontuaçãomapping(address => uint) public playerScores;function getScore(address _addr) public view returns (uint) {// Iremos retornar o score atual do jogadorreturn playerScores[_addr];}function setScore(address _addr, uint _score) public {// Atualiza o score do jogadorplayerScores[_addr] = _score;}}
Array (Matrizes)
Diferente dos mapas, as matrizes podem armazenar um valor único em uma lista de tipo único, podendo ser definida para qualquer tipo, address, uint, string ou qualquer outro disponível.
Uma array
pode ser definida com um tamanho único definido na compilação ou um tamanho dinâmico.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract Array {// Maneiras para definir uma arrayuint[] public arr; // Array de tamanho dinâmico// Array de tamanho dinâmico inicializada com os valores [1, 2, 3]uint[] public arr2 = [1, 2, 3];// Array de tamanho fixo, todos os elementos inicializam em 0uint[10] public myFixedSizeArr;function get(uint i) public view returns (uint) {return arr[i];}// Solidity pode retornar o array inteiro// Mas esta função deve ser evitada para arrays// que podem crescer indefinidamente em tamanhofunction getArr() public view returns (uint[] memory) {return arr;}function push(uint i) public {// Adiciona um valor na matriz// Isso aumentará o tamanho da matriz em 1arr.push(i);}function pop() public {// Remove o último valor adicionado// Isso diminuirá o tamanho da matriz em 1arr.pop();}function getLength() public view returns (uint) {// Retorna o tamanho da matrizreturn arr.length;}function remove(uint index) public {// Excluir o valor de um índice não irá diminuir o valor da matriz.// Irá definir o valor no índice para o valor padrão do tipo,// neste caso, o valor será setado para 0delete arr[index];}function examples() external {// Cria uma nova matriz na memória// Somente matrizes com tamanho fixo podem ser criadas assimuint[] memory a = new uint[](5);}}
Exemplos de remoção de elemento da matriz
Remova um elemento da matriz deslocando os elementos da direita para a esquerda
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract ArrayRemoveByShifting {// [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]// [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]// [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]// [1] -- remove(0) --> [1] --> []uint[] public arr;function remove(uint _index) public {// Verifica se o índice passado existe na matriz,// se não, ele retorna uma mensagem de errorequire(_index < arr.length, "índice fora do limite");for (uint i = _index; i < arr.length - 1; i++) {arr[i] = arr[i + 1];}arr.pop();}function test() external {arr = [1, 2, 3, 4, 5];remove(2);// [1, 2, 4, 5]assert(arr[0] == 1);assert(arr[1] == 2);assert(arr[2] == 4);assert(arr[3] == 5);assert(arr.length == 4);arr = [1];remove(0);// []assert(arr.length == 0);}}
Substitua um elemento da matriz copiando o último elemento para o índice que será removido
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract ArrayReplaceFromEnd {uint[] public arr;// A remoção de um elemento da matriz cria uma lacuna vazia.// Uma maneira de manter o array compacto é// mover o último elemento para o índice que foi removido.function remove(uint index) public {// Move o último elemento para o índice que foi removidoarr[index] = arr[arr.length - 1];// Remove o último elementoarr.pop();}function test() public {arr = [1, 2, 3, 4];remove(1);// [1, 4, 3]assert(arr.length == 3);assert(arr[0] == 1);assert(arr[1] == 4);assert(arr[2] == 3);remove(2);// [1, 4]assert(arr.length == 2);assert(arr[0] == 1);assert(arr[1] == 4);}}
Nesse artigo, apresentamos várias maneiras de utilizar o mapeamento de variáveis com o mapping
e o armazenamento de dados com o array