n*rd*nc*d

Solidity structs and arrays

This article is a continuation of "Let's write our first Solidity contract". I'd highly recommend you to read that first.

Bridge

On the previous article "Let's write our first Solidity contract", we create a contract called `MyContract` with a uint256 public variable called `luckyNumber`.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {
    uint256 public luckyNumber;

    function show(uint256 _luckyNumber) public {
        luckyNumber = _luckyNumber;
    }

    // `view` => Reading the value but not making any changes to the blockchain.
    function retrieve() public view returns(uint256) {
        return luckyNumber;
    }
}

Our `luckyNumber` variable can only store one single lucky number. What if we want to have multiple lucky numbers? or multiple lucky numbers of our friends?

There are several ways to do it, one of the ways is by creating a struct.

breathe...

Struct

Using struct, we can define our own type. It can be declared outside a contract and imported into another contract and is particularly useful for grouping together related data.

For our case, we can create a friends object that holds both their name and lucky number.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {
    // Create a new type called 'Friends'
    struct Friends {
        string name;
        uint256 luckyNumber;
    }

    uint256 public luckyNumber;
    /*
        Create a new `friend` variable using our new type `Friends`
        - `Friends` is the type (like `uint256`)
        - `public` is the visibility
        - `friend` is the variable name
    */
    Friends public friend = Friends({
        name: 'Oscar',
        luckyNumber: 9
    });
}
Struct 1
Remix: Create a new `friend` variable using our new type `Friends`

If you wonder why there are `0` and `1` in front of the `0: string: name Oscar` and `1: uint256: luckyNumber 9`. It's the index of the different variables, and in computer science, the list starts with no `0`.

Whenever you have a list of variables inside an object in Solidity, they will automatically be indexed.

We have created a new `friend` variable using our struct `Friends`. While this works, if we want to add more friends, we must copy-paste many friend variables with different names and luckyNumbers, like so:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {
    struct Friends {
        string name;
        uint256 luckyNumber;
    }

    Friends public friend = Friends({
        name: 'Oscar',
        luckyNumber: 9
    });

    Friends public friend2 = Friends({
        name: 'Ani',
        luckyNumber: 7
    });

    Friends public friend3 = Friends({
        name: 'Suchi',
        luckyNumber: 12
    });
    // ..etc
}

That doesn't look effective, don't you think?

breathe...

Array

An effective way to create a list is using an array data structure.

An array is a data structure that holds a list of other types and can be initialised with a fixed or dynamic size. It can hold primitive data types such as integers, boolean, and strings.

How does it look like?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {
    // Create a new type called 'Friends'
    struct Friends {
        string name;
        uint256 luckyNumber;
    }

    /*
        Create a new `friend` variable using our new type `Friends`
        - `Friends[]` is the type (like `uint256`).
          This means we want a type of an array of Friends
        - `public` is the visibility
        - `friend` is the variable name
    */
    Friends[] public friends;

}

Yes, you can also add `[]` to another type, like: `uint256[] public friendsList;`

breathe...

Before we move forward, I want to quickly add that the `Friends[]` type of an array is called Dynamic Array because the array size is not given at the array initialisation.

If we specify `Friends[4]`, this means that this array of friends can only be four people, hence called Fixed-sized Array.

For our case, we'll stick to the dynamic size array because we want to add an arbitrary number of friends to this array.

breathe...

Adding

Nice! We have a dynamic array variable called `friends`. Now let's create a function to add friends to the friends array.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {
    // Create a new type called 'Friends'
    struct Friends {
        string name;
        uint256 luckyNumber;
    }

    // Create a new `friend` variable using our new type `Friends`
    Friends[] public friends;

    /*
        Create a public function to add friend to the friends array
        - `_name` is a string memory for `name`
          (Data location must be "memory" or "calldata" for parameter in function)
        - `_luckyNumber` is uint256 for `luckyNumber`
    */
    function addFriend(string memory _name, uint256 _luckyNumber) public {
        // Adding a new friend to friends array
        Friends memory newFriend = Friends({ name: _name, luckyNumber: _luckyNumber });
        friends.push(newFriend);
    }
}

Essential to know capitalise and lowercase matters. When you see `Friends`, we are talking about our struct `Friends`. When we type `friends`, we refer to the variable friends (dynamic-sized array)..

On the arguments function, you see a new keyword `memory` (`string memory _name`). It's required to specify for the data location. To learn more, you could look at Solidity by example: Data Locations.

We could also improve our code above to be a one-liner. Like so:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {
    // Create a new type called 'Friends'
    struct Friends {
        string name;
        uint256 luckyNumber;
    }

    Friends[] public friends;

    function addFriend(string memory _name, uint256 _luckyNumber) public {
        /*
            Follow the pattern of the struct:
            [0] name
            [1] luckyNumber
            then we don't need to specify `{ name: _name, luckyNumber: _luckyNumber }`
        */
        friends.push(Friends(_name, _luckyNumber));
    }
}
breathe...

Helpers