Arrays, Structs & Mappings


#46

My solution for the second assignment:

pragma solidity ^0.4.0;

contract PersonContract {
    struct Person {
        string name;
        uint age;
    }
    Person[] persons;
    
    function addPerson(string _name, uint _age) {
        persons.push(Person(_name, _age));
    }
    
    function getAverageAge() view returns (uint) {
        uint numPersons = persons.length;
        if (numPersons == 0) // sanity check
            return 0;
        
        uint totalAge = 0;
        for (uint i=0; i<numPersons; i++) {
            totalAge += persons[i].age;
        }
        return totalAge / numPersons;
    }
}

#47

So in 1 setter function, you can’t return 2 values? like name + age to ‘get dog’
if you need to have this value, do we need to make a second function?

//already found my anwer. just add another getter.


#48

so basicaly like ‘private’ in c++?


#49

Hey @Guactoshi
your versions helps me a lot to understand solidity even faster , thanks :wink:


#50

in the next lecture of inheritance and visibility, it gets explained.
so internal means only visible in contract and child contracts.


#51

with using a datatype that allows decimals


#52

sorry,
ignore my reply.
I didn’t knew it wasn’t possible in solidity.


#53

Exercise:

pragma solidity ^0.4.0;
contract PersonContract {

struct Person {
    string name;
    uint age;
}

Person[] persons;

function addPerson(string _name, uint _age) {
    persons.push(Person(_name, _age));
}

function getAvgAge() returns (uint) {
    uint sum;
    for (uint i = 0; i < persons.length; i++){
        sum = sum + persons[i].age;
    }
    return sum / persons.length;
}

}


#54

My Solution to the Assignment:

pragma solidity ^0.4.0;



contract SignUp {
    
    struct Person {
        uint Age;
        string  Name;
    }

    uint membercount = 0;
    mapping (address => uint) userMap;
    Person[] people;
    
    function AddPerson(uint age, string name) public returns (bool) {
        
        if(userMap[msg.sender] == 0){
            membercount++;
            userMap[msg.sender] = membercount;
            people.push(Person(age, name));
            return true;
        } else {
            return false;
        }
    }
    
    function GetName() public view returns (string) {
        return people[userMap[msg.sender]-1].Name;
    }
    
    function GetAge() public view returns (uint) {
        return people[userMap[msg.sender]-1].Age;
    }
    
    function GetAvgAge() public view returns (uint) {
        uint totalYears = 0;
        for(uint i = 0; i < membercount; i++) {
            totalYears += people[i].Age;
        }
        return totalYears / membercount;
    }
}

#55

i can `t undestand why we call the (id-1) . anybody can give an explanation for small brains like mine?

got it : is because in uint id = dogs.push(Dog(_name , _age)); we get the element number and not the index in the array right?


#56

Thats right you have the length of the array returned and have to convert it to a zero based index, so you subtract 1.


#57
pragma solidity ^0.4.0;

contract DogContract{
    struct Dog {
        string name;
        uint age;
    }
    
    mapping(address => Dog[]) owners;
    
    function addDog(string _name, uint _age) public {
        owners[msg.sender].push(Dog(_name, _age));
    }
    
    function getDogName(uint _id) view public returns (string) {
        return owners[msg.sender][_id].name;
    }
    
    function getDogAge(uint _id) view public returns (uint) {
        return owners[msg.sender][_id].age;
    }
}

#58

pragma solidity ^0.4.11;

contract ArraysAndStructure{

struct Student{
    string name;
    uint age;
}

Student[] Students; 

function AddStudent(string _name , uint _age){
    Students.push(Student(_name , _age));
}

function GetStudent(uint _index) returns (string){
    return Students[_index].name;
}

}

in the above code , what if i have to return age also , do i have to use two return in the function GetStudent or something else ?? I am only able to return name of student … what if i want to return both(age and name)

plz tell filip


#59

You can actually return multiple values inside what’s called a tuple. The GetStudent function would look like this

function GetStudent(uint _index) returns (string, uint){
    return (Students[_index].name, Students[_index].age);
}

You can learn more here: http://solidity.readthedocs.io/en/latest/control-structures.html#destructuring-assignments-and-returning-multiple-values


#60

Took me a while to process everything properly in this lesson, had to re watch several times and do my own research, but I’m glad I got it! Here’s my code:

pragma solidity ^0.4.24;

contract People{
    struct Person{
        string name;
        uint age;
    }
    Person[] people;
    
    function addPerson(string _name, uint _age){
        people.push(Person(_name, _age));
    }
    function getAverageAge() view returns (uint){
        uint total;
        for (uint i = 0; i < people.length; i++){
            total += people[i].age;
        }
        return (total / people.length);
    }
}

I might do one which incorporates some mappings, that was still a little tough for me.


#61

Hello! Did you find any solution this? If say, the owner has multiple dogs in the same address, is there a way for us to retrieve a particular dog within the address, rather than just the latest dog?


#62

Hi all, just a bit of an update. I was exploring and messing around with the code. Like @cryptozz, I found that in Filip’s example of using the mapping ownerToDog[owner] = id;, if the same owner, or address input, has more than 1 dog, only the latest dog id will be retrieved.

I spent some time thinking about how can I possibly retrieve an earlier dog. Of course, we could simply do the following:

 function getDog(uint _arrayIndex) returns (string){
        return dogs[_arrayIndex].name;
    }

But that way, any address can retrieve another address’s array elements. So I’ve tried to combine the function above with the mapping function, and tested it in the following code:

pragma solidity ^0.4.24;

contract People{
    mapping (address => uint) personLocation;
    struct Person{
        string name;
        uint age;
        string occupation;
    }

    Person[] people;
    
    function addPerson(string _name, uint _age, string _occupation){
        address location = msg.sender;
        uint id = people.push(Person(_name, _age, _occupation));
        personLocation[location] = id;
    }
    
    function getName(uint _index) returns(string){
        address location = msg.sender;
        uint id = personLocation[location];
        return people[id - _index].name;
    }
    
    function getAge(uint _index) returns (uint){
        address location = msg.sender;
        uint id = personLocation[location];
        return people[id - _index].age;
    }
    
    function getOccupation(uint _index) returns (string){
        address location = msg.sender;
        uint id = personLocation[location];
        return people[id - _index].occupation;

    }

This way, I had success in accessing different elements within an array of a single address, and other addresses give an error when I tried to access it. Only problem now is that the argument that I put into each function, _index, is now ‘reversed’. When I input _index as 2, it will access the second last element. Not very intuitive, but I haven’t been able to find an alternative. My first time learn code is through this course, so I clearly have a long way to go. If anyone has a better solution, please teach me!

This was pretty interesting, and I learned a little more on why other addresses could not access the data of another address. Basically, when we run the line

uint id = personLocation[location];

If the address is not the sender of the contract, id is automatically zero. So in Filip’s example, when we try to retrieve

dogs[id -1].name;

We are trying to retrieve element -1, which is invalid, returning an error. Hope it isn’t too long winded, and that it might help someone better understand what is going on! :slight_smile:


#63

Good job! Great improvement of the contract. Very impressive if you started with 0 programming knowledge.

I see one issue with your code, but it’s a small one. You store all people in the same array. That means that anyone could possibly go through every entry of the array to find all of the people.

The more optimal way would be to have the mapping point to an array of people. Like this:

mapping (address => Person[]) personLocation;

By doing that every address would point to it’s own array where you can store multiple people or dogs.

Let me know if you understand what I mean.


#64

Hi Filip! Thank you for the encouragement, it means a lot! Hopefully I can do even better in time to come haha :slight_smile:

I see! I’m looking up the mapping to an array right now. Once I understand I’ll try to put it into the code and see how it works out!

Update

So I found this code about mapping to arrays that looks like this

pragma solidity^0.4.24;

contract Bar{
    struct Foo{
    uint x;
    }

    mapping(uint => Foo[])foo;

    function add(uint id, uint _x)public{
    foo[id].push(Foo(_x));
    }
    
    function get(uint id, uint index) public returns(uint){
    return foo[id][index].x;
    }
}

Is this what you meant? It kind of looks like a 2D-array thing to me, where we access an element from a few arrays. I imagine this can be put into the code for different groups of people (maybe different arrays can represent different groups of people?)

As for the issue, did you mean all addresses have a shared array? I believe my code is able to do one array per contract, and each contract can only access their own array, is that right? Anyone with the right address can go through every entry of the array to find all of the people, but only in that particular address, but not others? I ran a trial on it and it seems to be the case.


#65

Yes, that is exactly what I meant. Good job looking it up.

As for the previous code. All people were stored in the same array. You did not create multiple arrays for each address you used a mapping to find the position in the array. The code would still work as expected, but it would be more secure if you replaced it with a mapping to Arrays.