19

Posted in relation to the blog initiative.


Simple and appropriate data organization can challenge Solidity newcomers. It wants us to organize everything in ways many of us aren’t accustomed to. Here are some simple and useful patterns in increasing order of capability.

Simple List using Array

Strengths

  • In the order of declaration (chronological)
  • Provides a count (rows)
  • Random access by Row Number (not ID)

Weaknesses

  • No random access by Id
  • No assurance of uniqueness.
  • No check for duplicates.
  • Uncontrolled growth of the list
    contract simpleList {

        struct EntityStruct {
            address entityAddress;
            uint entityData;
            // more fields
        }

        EntityStruct[] public entityStructs;

        function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
            EntityStruct memory newEntity;
            newEntity.entityAddress = entityAddress;
            newEntity.entityData    = entityData;
            return entityStructs.push(newEntity)-1;
        }

        function getEntityCount() public constant returns(uint entityCount) {
            return entityStructs.length;
        }
    }

Mapping with Struct

Strengths

  • Random Access by unique Id
  • Assurance of Id uniqueness
  • Enclose complex arrays, mappings, structs within each “record”

Weaknesses

  • Unable to enumerate the keys
  • Unable to count the keys
  • Needs a manual check to distinguish a default from an explicit record

contract mappingWithStruct {

    struct EntityStruct {
        uint entityData;
        bool isEntity;
    }

    mapping (address => EntityStruct) public entityStructs;

    function isEntity(address entityAddress) public constant returns(bool isIndeed) {
        return entityStructs[entityAddress].isEntity;
    }

    function newEntity(address entityAddress, uint entityData) public returns(bool success) {
        if(isEntity(entityAddress)) throw; 
        entityStructs[entityAddress].entityData = entityData;
        entityStructs[entityAddress].isEntity = true;
        return true;
    }

    function deleteEntity(address entityAddress) public returns(bool success) {
        if(!isEntity(entityAddress)) throw;
        entityStructs[entityAddress].isEntity = false;
        return true;
    }

    function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
        if(!isEntity(entityAddress)) throw;
        entityStructs[entityAddress].entityData = entityData;
        return true;
    }
}

Array of Structs with Unique Ids

Strengths

  • Random Access by row number
  • Assurance of Id uniqueness
  • Enclose complex arrays, mappings and structs within each “record”

Weaknesses

  • No random access by Id
  • Uncontrolled growth of the list
contract arrayWithUniqueIds {

    struct EntityStruct {
        address entityAddress;
        uint entityData;
    }

    EntityStruct[] public entityStructs;
    mapping(address => bool) knownEntity;

    function isEntity(address entityAddress) public constant returns(bool isIndeed) {
        return knownEntity[entityAddress];
    }

    function getEntityCount() public constant returns(uint entityCount) {
        return entityStructs.length;
    }

    function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
        if(isEntity(entityAddress)) throw;
        EntityStruct newEntity;
        newEntity.entityAddress = entityAddress;
        newEntity.entityData = entityData;
        knownEntity[entityAddress] = true;
        return entityStructs.push(newEntity) - 1;
    }

    function updateEntity(uint rowNumber, address entityAddress, uint entityData) public returns(bool success) {
        if(!isEntity(entityAddress)) throw;
        if(entityStructs[rowNumber].entityAddress != entityAddress) throw;
        entityStructs[rowNumber].entityData    = entityData;
        return true;
    }
}

Mapped Structs with Delete-enabled Index

Strengths

  • Random Access by unique Id or row number
  • Assurance of Id Uniqueness
  • Enclose complex arrays, mappings and structs within each “record”
  • Count the records/Ids
  • Enumerate the Ids
  • Ability to logically control the size of the active list

Weaknesses

  • Marginally increased code complexity
  • Higher storage cost than alternatives
  • Key list is inherently unordered

Detailed description: https://medium.com/@robhitchens/solidity-crud-part-1-824ffa69509a#.20k69nqrc

contract mappedWithUnorderedIndexAndDelete {

    struct EntityStruct {
        uint entityData;
        uint listPointer;
    }

    mapping(address => EntityStruct) public entityStructs;
    address[] public entityList;

    function isEntity(address entityAddress) public constant returns(bool isIndeed) {
        if(entityList.length == 0) return false;
        return (entityList[entityStructs[entityAddress].listPointer] == entityAddress);
    }

    function getEntityCount() public constant returns(uint entityCount) {
        return entityList.length;
    }

    function newEntity(address entityAddress, uint entityData) public returns(bool success) {
        if(isEntity(entityAddress)) throw;
        entityStructs[entityAddress].entityData = entityData;
        entityStructs[entityAddress].listPointer = entityList.push(entityAddress) - 1;
        return true;
    }

    function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
        if(!isEntity(entityAddress)) throw;
        entityStructs[entityAddress].entityData = entityData;
        return true;
    }

    function deleteEntity(address entityAddress) public returns(bool success) {
        if(!isEntity(entityAddress)) throw;
        uint rowToDelete = entityStructs[entityAddress].listPointer;
        address keyToMove   = entityList[entityList.length-1];
        entityList[rowToDelete] = keyToMove;
        entityStructs[keyToMove].listPointer = rowToDelete;
        entityList.length--;
        return true;
    }

}

There are certainly more ways of combining structs, arrays and mappings. Do you have favorite or interesting pattern? Show us how it works!

closed as off-topic by Afri Mar 15 '17 at 21:46

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "This question does not appear to be about Ethereum Stack Exchange or the software that powers the Stack Exchange network, within the scope defined in the help center." – Afri
If this question can be reworded to fit the rules in the help center, please edit the question.

  • Kudos if anyone can get the formatting to cooperate. – Rob Hitchens B9lab Mar 12 '17 at 17:31
  • Closed as per Stack Exchange Staff suggestion. – Afri Mar 15 '17 at 21:47

Browse other questions tagged .