8

Good day.

I've got an array of structs such as:

User[] public users;


struct User{
    uint idNum;
    string name;
    address userAddress;
}

And I'm trying to get the array of users with web3.js using:

contract = web3.eth.contract([ABI]).at("0x0..");
var users = contract.users();

So far everything works fine, but when I'm trying to get the size of the array I always gets 3 - I assume it only counts how many fields each user have but what I want is to count how many users are there.

I understand the The variable "users" (in JS) is a function and not an array per se, but I just can't find any way to directly interact with the array.

2
  • Try var users = contract.users.length in your .js file,
    – niksmac
    Commented Apr 17, 2016 at 11:11
  • The result is 3 (even though the array is has 7 users). I can get the information on any specific user by typing his number manually contract.users(4) -> {idNum:4, name: "number 4", userAddress: "0x0.."}
    – shultz
    Commented Apr 17, 2016 at 11:38

2 Answers 2

9

Try the following:

function getUsersCount() public constant returns(uint) {
    return users.length;
}

function getUser(uint index) public constant returns(uint, string, address) {
    return (users[index].idNum, users[index].name, users[index].userAddress);
}

You should then be able use the statements:

> var numberOfUsers = contract.usersCount();
undefined
> numberOfUsers
5
> var user0 = contract.users(0)
[123, "User 123", "0xbeefee3ad17cd9aca6fd5343a53a513685c7e0ed"]
> var user1 = contract.getUser(1)
[234, "User 234", "0xbeefee3ad17cd9aca6fd5343a53a513685c7e0ed"]
> var lastUser = arrayStruct.users(numberOfUsers-1)
undefined
> lastUser
[567, "User 567", "0xbeefee3ad17cd9aca6fd5343a53a513685c7e0ed"]

And responding to the comment by Nikhil M, the statement below always returns 0 for me. I don't know if it is a design feature or a bug.

> var users = contract.users.length
undefined
> users
0



Gas Cost

The call to contract.usersCount(), contract.users(x) and contract.getUser() don't change the blockchain data and don't cost gas. From Greeter,

Run the Greeter In order to call your bot, just type the following command in your terminal:

greeter.greet();

Since this call changes nothing on the blockchain, it returns instantly and without any gas cost. You should see it return your greeting:

This is the contract code that I used:

contract ArrayStruct {

    struct User {
        uint idNum;
        string name;
        address userAddress;
    }

    User[] public users;

    function addUser(uint _idNum, string _name, address _userAddress) public returns(uint) {
        users.length++;
        users[users.length-1].idNum = _idNum;
        users[users.length-1].name = _name;
        users[users.length-1].userAddress = _userAddress;
        return users.length;
    }

    function getUsersCount() public constant returns(uint) {
        return users.length;
    }

    function getUser(uint index) public constant returns(uint, string, address) {
        return (users[index].idNum, users[index].name, users[index].userAddress);
    }
}

You will have to pay the gas for inserting the contract into the blockchain as shown by the gas: 1000000 field in the following statement:

var contract = arrayStructContract.new({
  from:web3.eth.accounts[0],
  data: arrayStructCompiled.ArrayStruct.code,
  gas: 1000000
}, function(e, contract) {
    if (!e) {
      if(!contract.address) {
        console.log("Contract transaction send: TransactionHash: " +
          contract.transactionHash + " waiting to be mined...");
      } else {
        console.log("Contract mined! Address: " + contract.address);
        console.log(contract);
      }
    }
})

And you will have to pay the gas when you change the blockchain data, as shown by the gas: 500000 in the following statements:

contract.addUser(123, "User 123", eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: arrayStructCompiled.ArrayStruct.code, 
  gas: 500000
});

contract.addUser(234, "User 234", eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: arrayStructCompiled.ArrayStruct.code, 
  gas: 500000
});
4
  • This is a good approach to the problem, But I was hopping to get the data using only JS in order to save some gas (users.length will be updated from time to time).
    – shultz
    Commented Apr 17, 2016 at 11:50
  • 2
    The call to contract.usersCount(), contract.users(x) and contract.getUser() don't change the blockchain data and don't cost gas. Commented Apr 17, 2016 at 11:54
  • but won't it increase the deployment cost? and what will happen when a user will be added/removed?
    – shultz
    Commented Apr 17, 2016 at 12:07
  • 1
    I've added the details about the deployment cost to the answer under Gas Cost Commented Apr 17, 2016 at 12:16
1

Here's code I use...probably just a work around, but from my node console I use:

var i = 4;
var j = 0;
while (i>3) {var x = contract.users.call(j);j++;var i = x.length} 
j-1

So then that last j-1 should print out the length of your list


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.