You check that a value is defined in the mapping by checking it is not zero.
If an explicit setting of zero has meaning for your application, you need auxiliary data (or structure) to track when a value of zero has been explicitly set.
A lightweight approach would be to add a bool property to the struct (say named initialized), and to set it to true when the zero is explicitly set. As all bools are false (0) by default, you can check against the value true.
Alternatively, check that each member of the struct is zero. If a member is a string, cast it to bytes and then check its length is zero.
For an example see How to test if a struct state variable is set
Another data structure or mapping may be needed depending on the application.
Here is a related example on using a pair to check the meaning of zero:
contract C {
uint[] counters;
function getCounter(uint index)
returns (uint counter, bool error) {
if (index >= counters.length) return (0, true);
else return (counters[index], false);
}
function checkCounter(uint index) {
var (counter, error) = getCounter(index);
if (error) { ... }
else { ... }
}
}
Answer from eth on Stack ExchangeYou check that a value is defined in the mapping by checking it is not zero.
If an explicit setting of zero has meaning for your application, you need auxiliary data (or structure) to track when a value of zero has been explicitly set.
A lightweight approach would be to add a bool property to the struct (say named initialized), and to set it to true when the zero is explicitly set. As all bools are false (0) by default, you can check against the value true.
Alternatively, check that each member of the struct is zero. If a member is a string, cast it to bytes and then check its length is zero.
For an example see How to test if a struct state variable is set
Another data structure or mapping may be needed depending on the application.
Here is a related example on using a pair to check the meaning of zero:
contract C {
uint[] counters;
function getCounter(uint index)
returns (uint counter, bool error) {
if (index >= counters.length) return (0, true);
else return (counters[index], false);
}
function checkCounter(uint index) {
var (counter, error) = getCounter(index);
if (error) { ... }
else { ... }
}
}
There's really no such thing as "empty". An uninitialized index of the mapping is simply equal to the " zero" value of the proper type.
To check if a value has been assigned, just check if balances[msg.sender]== 0. If a user accesses the contract, but the balance should be 0, you can use a address => int256 mapping, and use -1 for 0 balances
blockchain - how to initialize Empty array in stuct [Solidity] - Stack Overflow
solidity - Check if a struct is empty - Ethereum Stack Exchange
Creating an empty array to intitialise new struct
How about:
Cart storage c = carts[msg.sender]; c.total = 0; //whatever else you need to store in the newly created cart;
In solidity, all values in a mapping are virtually initialized, so you can straight access them and their members.
More on reddit.comif statement - How to test for an empty structure being returned via a Solidity function()? - Stack Overflow
I've been sorting out a relatively complex smart contract and running across an odd problem that I don't know how to deal with. The core concept is demonstrated by this vastly simplified example.
pragma solidity ^0.4.14;
contract ShoppingCart {
mapping(address => Cart) carts;
struct Cart {
uint total;
uint dateBought;
Purchase[] purchases;
}
struct Purchase {
uint productCode;
uint price;
}
function createCart() {
carts[msg.sender] = Cart('Target', 0, now, Purchase[]);
}
function addProductToCart(uint productCode, uint price){
carts[msg.sender].purchases.push(Purchase(productCode, price));
carts[msg.sender].total += price;
}
}The issue lies in this createCart example. Creating a blank purchase array is not sufficient to satisfy the function required in the struct, you get the following compiler error.
Invalid type for argument in function call. Invalid implicit conversion from type(struct ShoppingCart.Purchase memory[] memory) to struct ShoppingCart.Purchase memory[] memory requested.
For a start this compiler error is utterly useless. It's literally saying I can't convert from one type to an identical type. I've heard of similar issues coming up when importing, but this doesn't seem related.
I've seen a few theoretical solutions, but they were frankly awful, and there has to be a nice solution. How do you initialise a struct that contains an array? Please don't link me to the docs. I've read them intently and if they have a solution I don't know what i'm looking for.
Truffle v4.0.0-beta.0 (core: 4.0.0-beta.0) Solidity v0.4.15 (solc-js)
How about:
Cart storage c = carts[msg.sender];
c.total = 0;
//whatever else you need to store in the newly created cart;
In solidity, all values in a mapping are virtually initialized, so you can straight access them and their members.
Thanks, but I don't care about that. That isn't the issue. I wasn't even going to include that function it was just a usage example.
It was the specific "createCart" function I'm having issues with.
edit: just to stop anyone else getting distracted, I've fixed that line and a few other trivial things.
There is no need to initialize storage arrays in Solidity. Only memory arrays has to be initialized before usage.
So in your case, no need to initialize x inside Bar as long as you are not assigning a value to one of the x indexes inside your foobar. Actually, making initialization in your code will consume gas for no reason.
The following code works well for your case:
function foobar(address a) public {
Bar memory b;
b.owner = a;
//When 'b' is pushed to 'bars' array:
// (1) 'b' will be converted from memory to storage.
// (2) And 'x' inside it will be initialized automatically.
bars.push(b);
}
However, if you need to access x and push some value you can use push:
function foobar2(address a, uint x0) public {
Bar memory b;
b.owner = a;
bars.push(b);
//Trying: b.x[0] = x0; will generate error. Since, b.x is a memory array that is not initialized!
bars[bars.length - 1].x.push(x0); //This will work fine!
}
Actually, you can still initialize your memory array, as an empty one, as follow:
function foobar3(address a) public {
Bar memory b = Bar(a, new uint); //Thanks to "James Duffy" for his answer.
bars.push(b);
}
Last thing to mention is that: If you have multi values, that you need to insert to your x array, then you can do this as follow:
function foobar4(address a, uint[] _x) public {
Bar memory b = Bar(a, _x);
bars.push(b);
}
Or as follow. But, this will consume more gas:
function foobar5(address a, uint[] _x) public {
Bar memory b;
b.owner = a;
bars.push(b);
Bar storage c = bars[bars.length - 1]; // Get the newly added instance of the storage struct
for (uint i = 0; i < _x.length; i++) {
c.x.push(_x[i]);
}
Check full code at the nice EthFiddler: https://ethfiddle.com/fQI6Khgz3E
Working fiddle:
https://ethfiddle.com/Yn6fLiAjto
One way to make this work is to create your temporary Bar in memory before pushing it to bars (Bar memory b instead of Bar storage b). Solidity will then automatically copy the struct data from memory to storage. See: https://ethereum.stackexchange.com/a/4476/22415 for more details.
Also new uint[] is a function which requires a size argument, e.g. new uint.
So in full, that line should read Bar memory b = Bar(a, new uint);