Make Structs, Enums, Constructors and Mappings work together - struct

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)

Related

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;

Create new this on method call

This may be a stupid question, but is it possible to create a new this on a method call of a class?
E.g:
const foo = new Foo();
console.log(foo.a(1).b(2));
// for example, outputs 3 (1+2)
// the a method will create a new namespace and attach 1 to it, and b will use that new namespace
console.log(foo.b(2));
// this will result in an error, as there is no new namespace from the a method anymore, so b cannot add to anything?
Maybe this is too hard to understand, sorry.
class Foo {
a(number) {
this.a = number;
return this;
}
b(number) {
return this.a + number;
}
}
This would be the code where it uses the same this variable - this doesn't fit what I wanted but is what I currently have.
// pseudo
class Foo {
a(number) {
const uniqueVariable = number
return uniqueVariable
// it'll somehow pass the number from this method to the next method
}
// where it can be used with the second method's input
b(uniqueVariable, number) {
return uniqueVariable + number
}
}
foo.a(1).b(2) = 3
This example would obviously cause an error because the return value of a() a number, not something to use a method on again.
Please let me know if I need to explain further -- I'm having some struggle explaining it properly.
If the intention is that foo.a(1).b(2) changes foo, or if you don't mind changing foo, the other answers here work.
But if you only want foo.a(1).b(2) to return 3 without modifying foo, then you need to return a new Foo.
Now, if you really hell bent on having console.log() print 3 rather than something like Foo { value: 3 }, you can also customize inspect() (given that the question is tagged with node.js).
All together:
const util = require('util');
class Foo {
constructor(value) {
this.value = value || 0;
}
add(value) {
return new Foo(this.value + value);
}
a(value) {
return this.add(value);
}
b(value) {
return this.add(value);
}
[util.inspect.custom]() {
return this.value;
}
}
const foo = new Foo();
console.log(foo);
console.log(foo.a(2).b(1));
console.log(foo);
Output:
0
3
0
On my solution, I decided to create two variables to hold the values of each method. (https://jsbin.com/wozuyefebu/edit?js,console)
The a() method will return a number if the isSingle parameter is set to true. If not, it will return the this object, allowing you to chain the b() method. This is might be a hack but I believe it solves your problem.
I write about Javascript and web development on my blog :) https://changani.me/blog
class Foo {
constructor() {
this.aValue = 0;
this.bValue = 0;
}
/**
* #param {Number} value
* #param {Boolean} isSingle
* #returns {Object/Number}
*/
a(value = 0, isSingle = false) {
this.aValue = value;
return isSingle ? this.aValue : this;
}
/**
* #param {Number} value
* #returns {Number}
*/
b(value = 0) {
this.bValue = this.aValue + value;
return this.bValue;
}
}
const x = new Foo();
console.log("Should return 3: ", x.a(2).b(1));
console.log("Should return an 2: ", x.a(2, true));
console.log("Should return an instance of the object: ", x.a(2));
console.log("Should return 1: ", x.b(1));
console.log("Should return 0: ", x.a().b());
(https://jsbin.com/wozuyefebu/edit?js,console)
If you want to be able to invoke methods on return value of methods, then, you should return this from those methods. However, you will need an additional method, say value() to actuall get the result of sum.
A possible way is show below.
class Foo {
_a = 0;
_b = 0;
a(number) {
this._a = number;
return this;
}
b(number) {
this._b = number;
return this;
}
value() {
return this._a + this._b;
}
}
const foo = new Foo();
console.log(foo.a(1).b(2).value());
console.log(foo.b(5).value());

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

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);
}

Is it possible to have multiple mappings map to the same struct in Solidity?

What I'm trying to achieve is to have two mappings of struct value type pointing to the same struct reference, so I can lookup and edit a specific struct instance in two ways. However, updating the struct in one mapping does not seem to update the struct in the other one. Here is my simplified contract to illustrate the idea:
contract Example {
mapping(uint => Pool) public poolsByDay;
mapping(uint => Pool) public poolsById;
constructor(uint day) public {
for (uint i = 1; i <= day; i++) {
Pool memory pool = Pool({
id: i,
amount: 0
});
poolsByDay[i] = pool;
poolsById[i] = pool;
}
}
function deposit(uint day, uint amount) external {
Pool storage pool = poolsByDay[day];
pool.amount += amount;
}
}
Notice that the keys for poolsByDay may change every day. And I want to be able to lookup a pool either by day or by ID.
Here is my test:
const example = await Example.new(7)
const day = 1
const amount = 100e18
await example.deposit(day, amount.toString())
const pool = await example.poolsByDay(term)
const anotherPool = await example.poolsById(pool.id)
assert.equal(pool.amount, amount) // succeeded
assert.equal(anotherPool.amount, amount) // failed
From what I understand, Solidity struct is a reference type. So I'm expecting the modification of one pool will be reflected in both mappings poolsByDay and poolsById, but it's not. Is it that I failed to initialize two mappings correctly?
No, the two mappings will point to distinct structs, so you'll need to handle the indirection yourself, e.g. by using a mapping from day to ID:
contract Example {
mapping(uint => uint) public poolsByDay;
mapping(uint => Pool) public poolsById;
constructor(uint day) public {
for (uint i = 1; i <= day; i++) {
poolsById[i] = Pool({ id: i, amount: 0 });
poolsByDay[i] = i;
}
}
function deposit(uint day, uint amount) external {
Pool storage pool = poolsById[poolsByDay[day]];
pool.amount += amount;
}
}
(In this contrived example, they seem to both use the same keys, but I assume in your real code there's a reason for having two mappings.)

Find all objects in List<T> with same property value

I have a class Card, which has property Rank (1,2,3 etc) and I have a list of Cards and I wish to find all the Cards with the same Rank value in that list.
The list of Cards is sorted by Rank beforehand.
What would be the fasted way in .NET (using LINQ, if possible) to find all the objects with the same property value.
I don't know what the property value is beforehand, so I have to go through and compare. Until now I've been looping and keeping references to previous objects to compare, but I was wondering if there is not some easier way to do this?
Point is I need to be able to find N elements in a list with the same property value.
There might not be, but I thought I'd still ask.
You could group the cards by rank:
public class Card
{
public int Rank { get; set; }
}
class Program
{
static void Main()
{
var cards = new[]
{
new Card { Rank = 1 },
new Card { Rank = 2 },
new Card { Rank = 3 },
new Card { Rank = 2 },
new Card { Rank = 1 },
};
var groups = cards.GroupBy(x => x.Rank);
foreach (var group in groups)
{
Console.WriteLine("Cards with rank {0}", group.Key);
foreach (var card in group)
{
Console.WriteLine(card.Rank);
}
}
}
}
easiest would be to use linq
var results = myCardList.Select( c=>c.rank == myRank );
then you can iterate or convert to list.
Use Enumerable.ToLookup. The cards does not have to be sorted beforehand.
If you have a class
public class Card
{
public string Name { get; set; }
public int Rank { get; set; }
}
Then you can create a Lookup that groups per Rank like this
var cards = new Card[]{
new Card{Rank = 1, Name = "A"},
new Card{Rank = 1, Name = "B"},
new Card{Rank = 2, Name = "C"},
new Card{Rank = 3, Name = "D"},
};
var cardsByRank = cards.ToLookup(card => card.Rank);
foreach (var cardRankGroup in cardsByRank)
{
Console.WriteLine("KEY: " + cardRankGroup.Key);
foreach (var card in cardRankGroup)
{
Console.WriteLine(" Card: " + card.Name);
}
}
with the output
KEY: 1
Card: A
Card: B
KEY: 2
Card: C
KEY: 3
Card: D
Edit
if you want to extract all cards for a given Rank you can use rank as index to the lookup
foreach (var card in cardsByRank[2])
{
Console.WriteLine(" Card: " + card.Name);
}
Hey Tony, are you looking for something link this?
List<Card> rankedCards = cards.Where(o=> o.Rank == rank).ToList()
This should return a list of Cards that match the variable "rank" which I assume contains an int containing the value of Rank you want to match.

Resources