Is the mapping in solidity of one key on multiple structs of same type possible? - struct

I am trying to map one address on multiple structs of same type, which belongs to the same address. How can I do this, if I want to choose any of the "stored" structs for one address on request afterwards?
I created a struct called Prescription, and a mapping with the patients address. So what I really want is to map the patients address to several Prescription-structs.
struct Prescription {
address patients_address;
string medicament;
string dosage_form;
uint amount;
uint date;
}
mapping (address => Prescription) ownerOfPrescription;
address [] public patients;
function createPrescription(address patients_address, string medicament, string dosage_form, uint amount, uint date) public restricted {
var newPrescription = ownerOfPrescription[patients_address];
newPrescription.medicament = medicament;
newPrescription.dosage_form = dosage_form;
newPrescription.amount = amount;
newPrescription.date = date;
patients.push(patients_address) -1;
}
function getPre(address _address)view public returns (string, string, uint, uint){
return(
ownerOfPrescription[_address].medicament,
ownerOfPrescription[_address].dosage_form,
ownerOfPrescription[_address].amount,
ownerOfPrescription[_address].date);
}
Now I would have a function, where I can call all written Prescriptions for one patient. Actually I am able to call only the last written prescription for one address.

Sure, the value type of a mapping can be an array:
// map to an array
mapping (address => Prescription[]) ownerOfPrescription;
function createPrescription(...) ... {
// add to the end of the array
ownerOfPrescription[patients_address].push(Prescription({
medicament: medicament,
...
});
patients.push(patients_address);
}

Related

Make Structs, Enums, Constructors and Mappings work together

So I want to implement a simple CarShop contract in Solidity.
The contract should be initiated with a constructor where I input the current stock amount for the cars I already have in my Shop. I call these in the constructor (ToyotaCount, AudiCount, BmwCount)...
Then I think I need to create a struct that would store the CarCount and the CarType.
So I created an enum with (Toyota, Audi, Bmw)...
Finally, I would like to create this struct with the values CarCount from the constructor (as the initial state) together with carType of the cars from the enum... However, I am confused how exactly I should implement it and where I am going wrong.
Also, as a next step I want to implement a function called "AddCar" to update the values in the struct when I add some cars... for example I want to add 3 Audi cars...
Can you perhaps show me how I would need to correct my code, so the constructor, struct, enum work together. I would also really appreciate if you could point me to some similar projects or implementations.
This is my current Code. I think I initiated the constructor correctly. However, then something goes wrong with the interplay of struct and enum and constructor.
'''
contract CarShop {
address owner;
uint256 toyotaCount;
uint256 audiCount;
uint256 bmwCount;
constructor(uint256 _toyotaCount, uint256 _audiCount, uint256 _bmwCount) {
owner = msg.sender;
toyotaCount = _toyotaCount;
audiCount = _audiCount;
bmwCount = _bmwCount;
}
enum CarType {None, Toyota, Audi, Bmw}
struct Cars {
CarType carType;
uint count;
}
Cars public item;
Cars memory toyota = Cars(carType, toyotaCount)
}
'''
I made some changes to your contract and added some comments. Note that you should change how the cars are stored, because we use three Cars structs for the three different cars.
contract CarShop {
address owner;
uint256 toyotaCount;
uint256 audiCount;
uint256 bmwCount;
Cars public toyota;
Cars public audi;
Cars public bmw;
enum CarType {Toyota, Audi, Bmw}
struct Cars {
CarType carType;
uint count;
}
constructor(uint256 _toyotaCount, uint256 _audiCount, uint256 _bmwCount) {
owner = msg.sender;
toyotaCount = _toyotaCount;
audiCount = _audiCount;
bmwCount = _bmwCount;
// initialize the three cars with their count
toyota = Cars(CarType.Toyota, _toyotaCount);
audi = Cars(CarType.Audi, _audiCount);
bmw = Cars(CarType.Bmw, _bmwCount);
}
/**
* #dev Add car count function
* #param _carType type of the car: 0 for Toyota, 1 for Audi, 2 for Bmw
* #return _count increment car count
*/
function addCarCount(CarType _carType, uint256 _count) public {
require(msg.sender == owner, "Only owner can add car count");
if(_carType == CarType.Toyota) {
toyota.count += _count;
} else if(_carType == CarType.Audi) {
audi.count += _count;
} else if(_carType == CarType.Bmw) {
bmw.count += _count;
}
}
}
I deployed the contract with each 10 cars in store. I created a script that adds 3 cars to the audi struct.
import { ethers } from "hardhat";
async function main() {
const [owner] = await ethers.getSigners();
const contractAddress = process.env.CAR_CONTRACT_ADDRESS;
const contract = await ethers.getContractFactory("CarShop");
const contractInstance = await contract.attach(`${contractAddress}`);
const audi = await contractInstance.audi();
console.log(audi);
await contractInstance.connect(owner).addCarCount(1, 3);
const audiAfter = await contractInstance.audi();
console.log(audiAfter);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Results:
[
1,
BigNumber { value: "10" },
carType: 1,
count: BigNumber { value: "10" }
]
[
1,
BigNumber { value: "13" },
carType: 1,
count: BigNumber { value: "13" }
]
you cannot declare a memory variable in the contract level only in a function
Cars memory toyota = Cars(carType, toyotaCount)

Solidity struct mapping not stored in contract

I read many articles on how to use mappings, mappings in struct and came out with something that should be correct to me, based on a few threads.
I know that since solidity 0.7.0 things have changed with nested mappings in struct and did the following :
contract Test {
constructor() {
}
struct Bid {
uint auction_id;
address addr;
uint amount;
}
struct Auction {
uint id;
string dtype;
uint start_date;
uint end_date;
string label;
uint price;
uint amount;
bool closed;
mapping(uint => Bid) bids;
uint bidCount;
}
uint public auctionCount = 0;
mapping(uint => Auction) public auctions;
function createAuction( string memory plabel, string memory ptype, uint nbhours, uint pprice) external {
Auction storage nd = auctions[auctionCount];
nd.id = auctionCount;
nd.dtype = ptype;
nd.start_date = block.timestamp;
nd.end_date = block.timestamp+nbhours*60*60;
nd.label = plabel;
nd.price = pprice;
nd.amount = 0;
nd.closed = false;
nd.bidCount = 0;
auctionCount++;
}
}
Everything compiles fine, the createAuction transaction is succesful.
When checking on the contract in Ganache, auctionCount is incremented but I have no items added in the drawsmapping.
I also debugged the transaction with truffle and it goes through the function, assigning values through the execution of createAuction, but the changes are not persistent.
I even tried removing one string attribute since I read that when there are 3 it could have been a problem (ok, I have only 2 max ;)).
I must have missed something, but I'm out of options right now.
Thanks in advance for your help !
If you are talking about auctions mapping, ensure you use the correct index when accessing mapping items. In your case, the first Auction item you add to the mapping will have a 0 index. I tried your contract in Remix, and everything worked well.

Solidity Error: Version 0.8.0 : Struct containing a (nested) mapping cannot be constructed

struct Campaign {
address payable campaignOwner;
string campaignTitle;
string campaignDescription;
uint256 goalAmount;
uint256 totalAmountFunded;
uint256 deadline;
bool goalAchieved;
bool isCampaignOpen;
bool isExists;
mapping(address => uint256) contributions;
}
//stores a Campaign struct for each unique campaign ID.
mapping(uint256 => Campaign) campaigns;
function createCampaign(string memory _campaignTitle, string memory _campaignDescription, uint256 _goalAmount, uint256 _fundingPeriodInDays ) public {
++totalCampaigns;
uint256 period = block.timestamp + (_fundingPeriodInDays * 1 days);
Campaign memory aCampaign = Campaign(payable(msg.sender),_campaignTitle, _campaignDescription, _goalAmount, 0, period , false, true, true);
campaigns[totalCampaigns] = aCampaign;
}
I am using Solc version 0.8.0. When I try to create a Struct that contains mapping, I received an error:
Struct containing a (nested) mapping cannot be constructed.
When I use older versions of solc (0.5.8), the code compiles without problems. But this version is not supported with other solidity files and giving error as:
Source file requires different compiler version (current compiler is
0.8.13+commit.abaa5c0e.Emscripten.clang) - note that nightly builds are considered to be strictly less than the released version
You can not keep a struct containing a mapping in memory, and you can not initialize a storage object in a function, it must be a state variable. So what you can do is, get an object from your mapping, assign it to a local variable and modify it. Like this:
function createCampaign(string memory _campaignTitle, string memory _campaignDescription, uint256 _goalAmount, uint256 _fundingPeriodInDays ) public {
++totalCampaigns;
uint256 period = block.timestamp + (_fundingPeriodInDays * 1 days);
Campaign storage aCampaign = campaigns[totalCampaigns];
aCampaign.campaignOwner = payable(msg.sender);
aCampaign.campaignTitle = _campaignTitle;
aCampaign.campaignDescription = _campaignDescription;
aCampaign.goalAmount = _goalAmount;
aCampaign.totalAmountFunded = 0;
aCampaign.deadline = period;
aCampaign.goalAchieved = false;
aCampaign.isCampaignOpen = true;
aCampaign.isExists = true;
}

Solidity: problem creating a struct containing mappings inside a mapping

This is my code where i am trying to create a struct containing two mappings and insert the structs into a mapping:
pragma solidity ^0.7.2;
contract Campaign {
struct Usuario {
string id;
mapping(string => uint) debe;
mapping(string => uint) leDebe;
}
Usuario[] public usuarios;
uint numUsuarios;
mapping(string => Usuario) public circulo;
constructor () {
}
function usuarioPrueba(string memory id, string memory idDebe, uint valDebe, string memory idLeDebe, uint valLedebe) public {
usuarios.push();
Usuario storage newUsuario = usuarios[numUsuarios];
numUsuarios++;
newUsuario.id = id;
newUsuario.debe[idDebe] = valDebe;
newUsuario.leDebe[idLeDebe] = valLedebe;
circulo[id] = newUsuario;
}
}
but I am getting the following error at line 28 (circulo[id] = newUsuario;) on Remix:
TypeError: Types in storage containing (nested) mappings cannot be
assigned to. circulo[id] = newUsuario;
Thank you so much for the help beforehand and I am sorry for my english, I am from Spain and if the solution its just to obvious, I am kind of new to solidity and smart contracts.
Since v 0.7.0 you cannot assign structs containing nested mappings. What you can do instead is to create new instances like this one and then asign the values to the properties of the struct!
Usuario storage newUsuario = circulo[id];
numUsuarios++;
newUsuario.id = id;
newUsuario.debe[idDebe] = valDebe;
newUsuario.leDebe[idLeDebe] = valLedebe;

How to get an overview of created structs in a mapping for a specific address in solidity?

After I created some structs which belong to specific addresses I want to get an overview of structs with associated parameters regarding an address.
So what could I do for solving this problem?
If I am running my code inside remix, I get back only my first stored struct for an address. But I want to get back all stored structs for one address. I know that we can not iterate through a mapping, but maybe it is possible to make some index-counter for the array of structs to solve it? - So is it also possible to store the index of array in a variable?
pragma solidity ^0.4.17;
contract Prescribe {
struct Prescription {
address patients_address;
string medicament;
string dosage_form;
uint amount;
uint date;
//uint index_counter;
}
mapping (address => Prescription[]) public ownerOfPrescription;
address [] public patients;
function createPrescription(address patients_address, string
medicament, string dosage_form, uint amount, uint date) public
restricted {
ownerOfPrescription[patients_address].push(Prescription({
patients_address: patients_address,
medicament: medicament,
dosage_form: dosage_form,
amount: amount,
date: date
}));
patients.push(patients_address);
}
function getOverview(address patient) public view restricted
returns(string, string, uint, uint) {
for(uint i = 0; i < ownerOfPrescription[patient].length; i++) {
if(ownerOfPrescription[patient][i].patients_address == patient) {
return(ownerOfPrescription[patient][i].medicament,
ownerOfPrescription[patient][i].dosage_form,
ownerOfPrescription[patient][i].amount,
ownerOfPrescription[patient][i].date);
}
}
}
So I want to have the return-values of all separate structs of one address as in the function getOverview on the screen, but it gives me back only the first struct of an address
Well, it's returning only the first one, because after the statement
if(ownerOfPrescription[patient][i].patients_address == patient)
return true, your code is executing the return statement which will make the control to exit from the function, and no further statement will be executed.
Ok after a research I made the conclusion that it is still not possible to get an array of structs as a return value. One has only the possibility to access individual elements of the array right? - If there are any updates on this topic, I would be very grateful for a hint.
There is a way to return array of struct but it will cost a little bit more gas fees.
Below is an example
contract Test {
struct FlexiblePlan {
string token;
address _address;
}
struct Plans {
FlexiblePlan[] flexiblePlans;
}
Plans plans;
function createPlan(string memory _token, address _address) external {
plans.flexiblePlans.push(FlexiblePlan(_token, _address));
}
function getAllPlans() external view returns(Plans memory){
return plans;
}
}

Resources