Summary
Return the fields of the struct as separate return variables.
Edit: As of 2021, it's possible to return the struct directly. See this answer.
Example
I'm running this code in my local dev blockchain using the following command:
geth --datadir ~/devdata --dev --nodiscover \
--mine --minerthreads 1 --port 30301 \
--maxpeers 0 --verbosity 3 --rpc console
Your web frontend should be able to send transactions to insert the users, and call the functions to get the number of users and and the user info.
Sample Contract
contract SalaryInfo {
struct User {
uint salaryId;
string name;
string userAddress;
uint salary;
}
User[] public users;
function addUser(uint _salaryId, string _name, string _userAddress, uint _salary) public returns(uint) {
users.length++;
users[users.length-1].salaryId = _salaryId;
users[users.length-1].name = _name;
users[users.length-1].userAddress = _userAddress;
users[users.length-1].salary = _salary;
return users.length;
}
function getUsersCount() public constant returns(uint) {
return users.length;
}
function getUser(uint index) public constant returns(uint, string, string, uint) {
return (users[index].salaryId, users[index].name, users[index].userAddress, users[index].salary);
}
}
Flatten The Source Code
I use the stripCrLf
method (from How to load Solidity source file into geth) to transform the formatted source into a single line that can be inserted within the geth
console. Alternatively, search for a web page that will strip your line breaks from your code. Then assign your code to a JavaScript variable:
> var salaryInfoSource='contract SalaryInfo { struct User { uint salaryId; string name; string userAddress; uint salary; } User[] public users; function addUser(uint _salaryId, string _name, string _userAddress, uint _salary) public returns(uint) { users.length++; users[users.length-1].salaryId = _salaryId; users[users.length-1].name = _name; users[users.length-1].userAddress = _userAddress; users[users.length-1].salary = _salary; return users.length; } function getUsersCount() public constant returns(uint) { return users.length; } function getUser(uint index) public constant returns(uint, string, string, uint) { return (users[index].salaryId, users[index].name, users[index].userAddress, users[index].salary); }}'
Insert Contract Into The Blockchain
The compile the code:
> var salaryInfoCompiled = web3.eth.compile.solidity(salaryInfoSource);
Load the code into the blockchain:
> var salaryInfoContract = web3.eth.contract(salaryInfoCompiled.SalaryInfo.info.abiDefinition);
> var salaryInfo = salaryInfoContract.new({from:web3.eth.accounts[0], data: salaryInfoCompiled.SalaryInfo.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);
}
}
}
)
Wait for the following message to indicate that the contract has been mined:
I0505 09:12:15.712867 27030 xeth.go:1026] Tx(0x7747500b881c8da44efbc3b5d1c2c762f1cd52d2dd74050edbfed10e51a29d8a) created: 0x0bb1d7a6b31f7a7e23e6d902bac0eb5f9c721c54
Contract transaction send: TransactionHash: 0x7747500b881c8da44efbc3b5d1c2c762f1cd52d2dd74050edbfed10e51a29d8a waiting to be mined...
...
Contract mined! Address: 0x0bb1d7a6b31f7a7e23e6d902bac0eb5f9c721c54
[object Object]
Insert Users
And here we are adding 2 users to the contract:
> salaryInfo.addUser(123, "User 123", "123 drive way, the uncentralised kingdom", 100, {from:web3.eth.accounts[0], data: salaryInfoCompiled.SalaryInfo.code, gas: 500000});
"0x7c22797d6b7717beb398a65159b1009fba3bbc9e4917ee1584bed60ea74eac11"
> salaryInfo.addUser(234, "User 234", "234 drive way, the uncentralised kingdom", 200, {from:web3.eth.accounts[0], data: salaryInfoCompiled.SalaryInfo.code, gas: 500000});
"0xed197c9a6fbc70c19cc95bcdc6943e38736c080052e9e1a4f7562216d6de4c78"
Retrieve Data
Let's get the number of users:
> var numberOfUsers = salaryInfo.getUsersCount();
undefined
> numberOfUsers
2
Let's get the information for the first user:
> salaryInfo.getUser(0)
[123, "User 123", "123 drive way, the uncentralised kingdom", 100]
And the second user:
> var user2 = salaryInfo.getUser(1);
undefined
> user2
[234, "User 234", "234 drive way, the uncentralised kingdom", 200]
Based on my Debug observation:
I have tested that:
- Firstly I insert data into my map data-structure as I explained in my question.
- Secondly, I have returned the address of first mapped index, which is 0, this could be done for other indexes as well (0:10).
- Without a delete operation I can retrieve the data perfectly fine from the address of mapped Foo[]. But if I did the delete operation, Solidity gives an error.
This debug result: leads me to conclude that memory is freed or address does not contain any valid information.
Test code:
set_txn_hash = my_contract.transact().map_insert();
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
for x in range(0, 9):
for y in range(0, 9):
output = my_contract.call().get_(x, y);
print(output); //returns correct value.
set_txn_hash = my_contract.transact().map_remove_map();
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
set_txn_hash = my_contract.transact().get_map_address(0);
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
output = my_contract.call().get_map();
print(output);
output = my_contract.call().try_();
print(output); //Solidity gives error if delete map_remove_map() is called
Additional functions inside my Contract:
function map_insert(){
for(uint i = 0; i < 10; i++)
for(uint j = 0; j < 10; j++)
add(i, i*10 + j);
}
function map_remove_map(){
for(uint i = 0; i < 10; i++)
delete_(i);
}
function map_remove_map_item(){
for(uint i = 0; i < 10; i++)
for(uint j = 0; j < 10; j++)
delete_items(i, j);
}
function get_map_address(uint id) {
uint addr;
Foo[] a = foo[id];
assembly{
addr := a
}
map_address = addr;
}
function get_map() constant returns (uint){
return map_address;
}
function try_() constant returns (uint ){
uint addr = map_address;
Foo[] a;
assembly{
a := addr
}
return a[1].x;
}
Best Answer
Yes, you would be making a copy.
What you could do instead is to just have an array of
tests
and then map addresses to array indices.