With your currently defined variables, you can loop through all the existing store items until you've found the corresponding item, and then update it. If there's no corresponding item, you can create a new one:
function addProduct(uint id, uint quantity) public {
// loop through all `store` items until the item with the corresponding ID is found
for (uint i; i < store.length; i++) {
if (store[i].id == id) {
// corresponding item found - update quantity and early return
store[i].quantity += quantity;
return;
}
}
// no corresponding item found (the early return didn't invoke) - add the product to the store
store.push(Store(id, quantity));
}
However, the code above is very inefficient and expensive to run gas-wise. So I'd recommend to add a mapping of existing product IDs to the store array indexes. Then you can simply query the mapping and get the resulting info whether the product ID exists (the value of the mapping is non-zero) or not:
mapping (uint => uint) productIdToArrayIndex;
function addProduct(uint id, uint quantity) public {
uint arrayIndex = productIdToArrayIndex[id];
if (arrayIndex > 0) {
// the product is stored in the `store[arrayIndex]`
store[arrayIndex].quantity += quantity;
}
// no corresponding item found - add the product to the store, and add their index to the mapping
store.push(Store(id, quantity));
productIdToArrayIndex[id] = store.length - 1;
}
Answer from Petr Hejda on Stack Overflowsolidity - How to check if an array key exists? - Ethereum Stack Exchange
mapping - Solidity 0.4.26 check if element already exists in array - Ethereum Stack Exchange
ethereum - How to know if a specific value exists in the mapping table or not? - Stack Overflow
solidity - How to efficiently check if items in array A exist in array B? - Ethereum Stack Exchange
You can use an explicit exists field:
pragma solidity ^0.4.24;
contract MyContract {
struct Person {
uint age;
uint size;
bool exists;
}
// Index of a person is its ID.
Person[] persons;
event PersonAdded(uint indexed id, uint age, uint size);
function addPerson(uint _age, uint _size) public {
Person memory person = Person(_age, _size, true);
id = persons.push(person) - 1;
emit PersonAdded(id, _age, _size);
}
function removePerson(uint _id) public {
require(persons[_id].exists, "Person does not exist.");
delete persons[_id];
}
}
you can try to parse the array as following :
function exist (uint age_, uint size_) view returns (bool){
for (uint i; i< persons.length;i++){
if (persons[i].age==age_ && persons[i].size==size_)
return true;
}
}
In order to avoid For loops, You can add another mapping to check if a user exists or not. You can add mapping(string => bool) userExists; so your whole code will look like this:
pragma experimental ABIEncoderV2;
contract structWithMapping{
struct Data{
string[] user;
string[] catagory;
string[] data;
}
mapping(string => Data) mappedData;
mapping(string => bool) userExists;
string[] public dataArray;
function setUserData(string _user, string _catagory, string _data)public{
var addData = mappedData[_user];
addData.user.push(_user);
addData.catagory.push(_catagory);
addData.data.push(_data);
require(!userExists[_user]);
dataArray.push(_user) -1;
//dont forget to set the mapping value to true for userExists
userExists[_user] = true;
}
function getUsers() view public returns(string[]){
return dataArray;
}
function getUserData(string _user) view public returns(string[] memory, string[] memory, string[] memory){
return(mappedData[_user].user, mappedData[_user].catagory, mappedData[_user].data);
}
}
Reading your code I think I understood what you want to achieve, but I suggest to completely change the approach. Please note that using a smart contract costs money, so as a developer you should aim to create business logic that is both clean - so it cannot be misinterpreted or misleading - and light - so every transaction costs as few as possible.
To achieve that you need also to deep dive into the EVM and Solidity or Vyper.
That said, I think your entire smart contract should be transformed to this:
contract structWithMapping {
struct User {
string username;
string category;
string data;
bool initialized;
}
mapping(string => User) public users;
function setUserData(string memory _username, string memory _category, string memory _data) public{
require(!users[_username].initialized);
users[_username].username = _username;
users[_username].category = _category;
users[_username].data = _data;
users[_username].initialized = true;
}
}
Note also that Solidity adds getter functions at compile time for every public variable, so in this case a users function will be created. You can use that function passing the username as parameter to retrieve user data in the exact same way you would have called getUserData function.
I created a gist so you can directly experiment through the Remix IDE via this link.
There's no such thing as "existence" in a Solidity mapping.
Every key maps to something. If no value has been set yet, then the value is 0.
For your use case, hashstructs[n].fhash > 0 is probably sufficient. If you want to be explicit, add a bool exists to your struct and set it to true when you add something. Then use hashstructs[n].exists to check for existence.
If you want to check whether a key exists in the mapping then you can check the byte length. If the length is zero that means the key doesn't exist else key is present in the mapping. A Sample Example is:-
function checkUser(string memory user_id) public view returns(bool){
if(bytes(PassHash[user_id]).length>0){
return true;
}
else{
return false;
}
}
In Javascript, we might check for the existence of an object member like this:
if (object[key]) {
// do something
} else {
// do something else
}
I tried if(mapping[key]) { ... } in Solidity but that didn't work...
"Note that there is no type conversion from non-boolean to boolean types as there is in C and JavaScript, so if (1) { ... } is not valid Solidity." (https://github.com/ethereum/wiki/wiki/Solidity-Tutorial#control-structures).
mapping[key] == 0 is not a valid expression, so... how do we check for the existence of a key in a mapping?
Solidity provides no obvious way to do this.
One can contemplate an iterative process to search the list, but the gas cost will increase with the size of the list. This puts an upper bound on the size of the list; either by increasing transaction cost to an unacceptable level, or by exceeding the block gas limit so transactions can't run at all.
There are (at least) two general solutions.
Invert control so the client performs the iterative operation and interrogates contract functions one row at a time. It's possible to expose indexed lists (searchable) with event emitters. That may help, but in any case, performance will suffer with list size.
Use a mapping to facilitate random access in a single step.
Since mappings can't be enumerated and it's not possible to know how many keys have been set or what they are, it may be necessary to devise a system of pointers using both arrays and mappings to produce a "list" of randomly accessible items.
Have a look here for a pattern and better explanation: https://bitbucket.org/rhitchens2/soliditycrud/overview
Hope it helps.
In case someone still wants a solution to identify a string inside an array.
You first need to have a way to compare strings. You can do it using the following function:
// Function to compare 2 strings
function compareStrings(string memory firstString, string memory secondString) internal pure returns (bool) {
return keccak256(bytes(firstString)) == keccak256(bytes(secondString));
}
This will return true if the 2 inputs match.
Then we can use this function to iterate in the array and check if the string exists:
function findString(string[] memory array,string memory _string) internal pure returns (bool){
for (uint i = 0; i < array.length; i++) {
string memory stringToFind = array[i];
bool exists = compareStrings(stringToFind, _string) ;
if(exists == true) {
return true;
}
}
return false;
}
This will return true if the string is in the array and false if it isn't.
You can then use it like this:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract FindString {
string[] public colors = ["blue", "yellow", "red"];
function compareStrings(string memory firstString, string memory secondString) internal pure returns (bool) {
return keccak256(bytes(firstString)) == keccak256(bytes(secondString));
}
function findString(string[] memory array,string memory _string) internal pure returns (bool){
for (uint i = 0; i < array.length; i++) {
string memory stringToFind = array[i];
bool exists = compareStrings(stringToFind, _string) ;
if(exists == true) {
return true;
}
}
return false;
}
function findStringInArray(string memory _string) public view returns(bool){
return findString(colors, _string);
}
}
I also made a Solidity library to help with these kind of tasks:
- Soliditool
Array myArray is dynamically sized. You cannot safely try to access an element with an arbitrary index using the [] operator, you first need to check if the index is out of bounds. Here is how you could change your function in contract A to achieve this:
function isIndexExists(uint256 index) public view returns (bool) {
// If the index is out of bounds, then there is no such element
if (index >= myArray.length) {
return false;
}
// We know that there is an object with that index, so check its 'isExist' property
return myArray[index].isExist;
}
BTW, note that when you are checking some expression that returns a boolean, you can directly return that value. You don't need the redundant if-else, which takes 5 lines compared to this approach which only takes one line of code.
The error is caused by either of two reasons:
Out of bounds: You're accessing a non-existing index of the array in the
myArray[index]expression.Missing return: You're not returning any value in the
// ... logicsection of thetodo()function that promises to return abool.
Have a look here: Are there well-solved and simple storage patterns for Solidity?
You could.
function isMember(uint index) public view returns(bool isIndeed) {
return members[index].exists;
}
It's preferable to throw an error on an unacceptable state change rather than return false in most situations. So ...
function addBalance(uint index) public returns(bool success) {
require(isMember(index);
members[index] += msg.value; // (or _balance if you prefer)
return true;
}
If there is a good reason to return false and carry on:
function addBalance(uint index) public returns(bool success) {
if(!isMember(index) return false;
if(isMember(index)) member[index] += msg.value;
return true;
}
In my opinion it's almost always better to use the member address as the key instead of storing the structs in numbered rows. Like that, using arbitrary amounts instead msg.value as in your example:
pragma solidity ^0.4.24;
contract MembersContract {
struct Member {
uint balance;
bool exists;
}
mapping(address => Member) public members;
function isMember(address member) public view returns(bool isIndeed) {
return members[member].exists;
}
function addMember(address member) public returns(bool success) {
require(!isMember(member));
members[member].exists = true;
return true;
}
function addBalance(address member, uint amount) public returns(bool success){
require(isMember(member));
members[member].balance += amount;
return true;
}
}
Hope it helps.
Array access is guarded in Solidity. If you try to read past the end of the array, you'll get an invalid opcode error.
If you want to catch this condition yourself, you could do a bounds check first:
if (members.length > 1 && members[1].exists) {
...
}
Solidity doesn't provide a contains method, you'd have to manually iterate and check.
Using an array for what you're trying to achieve would be a highly inefficient pattern. The best and most cost efficient method is use a mapping data structure. Set the key to be the address and the value to be a boolean. Lists that are too long have the possibility of running out of gas when you're trying to iterate over them.
If you need to iterate through all the keys in the mapping, then you'd need to have an external database to get all the keys. The database can be populated and updated based on events from the smart contract (i.e. an event when the address is added or removed).
If I were you, I would use a pattern like this:
contract myWallets
{
mapping (address => bool) public Wallets;
function setWallet(address _wallet) public{
Wallets[_wallet]=true;
}
function contains(address _wallet) returns (bool){
return Wallets[_wallet];
}
}