Here you can ask questions related to this specific course section.
Am I understanding this right? Mapping addresses => struct is not ideal because a struct is initialized for every address call that happens so we canât tell if itâs a real entry or just an entry with default values. Is that just the nature of the data structure? Why wouldnât the code just read an error?
Hey @wilsonlee123, hope you are ok.
Is not about if its ideal or not to use a mapping from address to struct, is completely valid.
Is just to keep in mind that some variables will be initialized by default with a value, booleans for example are always start by default at âfalseâ.
So in case of mappings, is just to show you some few points to take into consideration, all mapping from address will start with default values, until you change the state of the values.
If you have any more questions, please let us know so we can help you!
Carlos Z.
Thanks, Carlos!
To dig into this a bit more, why does that happen? Is it just how the data structure is?
Hey @wilsonlee123
this is common to all programming languages that asks for a variable type.
Basically only javascript does not initialise variables by default.
Cheers,
Dani
function isEntity(address entityAddress) public view returns(bool isIndeed) {
if(entityList.length == 0) return false;
return (entityList[entityStructs[entityAddress].listPointer] == entityAddress);
}
function newEntity(address entityAddress, uint entityData) public returns(bool success) {
if(isEntity(entityAddress)) revert();
entityStructs[entityAddress].entityData = entityData;
entityList.push(entityAddress);
entityStructs[entityAddress].listPointer = entityList.length - 1;
return true;
}
I am a bit confused with some of Filips code here.
In the isEntity function, does the following code checks the index of the array or does it check to see how many total entries are in the array? if(entityList.length == 0) return false;
if the answer is the latter than the following code in newEntity function entityStructs[entityAddress].listPointer = entityList.length - 1; this sets the listPointer to match the index correct? Essentially the first entry in the entityList would be 1?.
Hey @RCV
In the isEntity function, does the following code checks the index of the array or does it check to see how many total entries are in the array? if(entityList.length == 0) return false;
This line if(entityList.length == 0) return false;
is checking the length of the entityList
array.
If the length is 0, it means that the array is empty so there is no need to check further and the function can immediately return false
.
entityStructs[entityAddress].listPointer = entityList.length - 1; this sets the listPointer to match the index correct? Essentially the first entry in the entityList would be 1?.
This sets the pointer to the index of the array that contains the user address.
Example with a newly deployed contract:
-
Add a new entry:
-
listPointer
for my address will now be0
which is the index of the array where my address is stored
entityStructs[entityAddress].listPointer = entityList.length - 1;
entityList.length = 1;
1 = 1 = 0
0 is the index.
Cheers,
Dani
Hi there.
In the contract of simple-mapping the code of filip is this way:
pragma solidity 0.8.0;
contract mappingWithStruct {
struct EntityStruct {
uint entityData;
bool isEntity;
}
mapping (address => EntityStruct) public entityStructs;
function newEntity(address entityAddress, uint entityData) public returns(bool success) {
if(isEntity(entityAddress)) revert();
entityStructs[entityAddress].entityData = entityData;
entityStructs[entityAddress].isEntity = true;
return true;
}
function isEntity(address entityAddress) public view returns(bool isIndeed) {
return entityStructs[entityAddress].isEntity;
}
function deleteEntity(address entityAddress) public returns(bool success) {
if(!isEntity(entityAddress)) revert();
entityStructs[entityAddress].isEntity = false;
return true;
}
function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
if(!isEntity(entityAddress)) revert();
entityStructs[entityAddress].entityData = entityData;
return true;
}
}
© 2021 GitHub, Inc.```
Why do we need the isEntity function? Im a bit confused⊠also the function deleteEntity does not delete data, just put the flag to false.
Am I missing something out?
I have rewrite the code on my point of view, what do u think about it?
pragma solidity 0.8.0;
contract mappingWithStruct {
struct EntityStruct {
uint entityData;
bool isEntity;
}
mapping (address => EntityStruct) public entityStructs;
function newEntity(address entityAddress, uint entityData) public returns(bool success) {
//if(isEntity(entityAddress)) revert();
require(entityStructs[entityAddress].isEntity == false);
entityStructs[entityAddress].entityData = entityData;
entityStructs[entityAddress].isEntity = true;
return true;
}
function deleteEntity(address entityAddress) public returns(bool success) {
//if(!isEntity(entityAddress)) revert();
require(entityStructs[entityAddress].isEntity == true);
entityStructs[entityAddress].isEntity = false;
return true;
}
function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
//if(!isEntity(entityAddress)) revert();
require(entityStructs[entityAddress].isEntity == true);
entityStructs[entityAddress].entityData = entityData;
return true;
}
}
My code does the same as Filips.
Hey @zero0_cero
isEntity()
checks if there is data in the struct EntityStruct
for the given address.
deleteEntity just set isEntity
to false so that when you call isEntity()
you will get false and your functions will revert.
Regards,
Dani
Ok so Im trying to get a better grasp on this assignment so I wrote this code based on the example philip used and i included a few comments on some of the concepts I dont understand. If anyone could take the time togive some advice I would really appreciate it
pragma solidity 0.8.0;
contract deletingFromArray{
struct Rstruct{
uint data;
uint listpointer;
}
mapping (address => Rstruct) public Rstructs;
address[] public Rlist;
function isR(address Raddress) public view returns (bool isIndeed) {
if (Rlist.length==0) return false;
**return (Rlist[Rstructs[Raddress].listpointer]==Raddress);** // I understand that we reach into the data types inside the struct
} // but how was that set to the local variable Raddress?
function getRCount() public view returns**(uint so_this_can_b_anything)**{ //Why can this be named anything? Is this calldata?
return Rlist.length;
}
function newR(address Raddress, uint data) public returns (bool succes){ //Error check? I guesse because bools are cheper than require?
if (isR(Raddress)) revert (); //So we call the function we declared earlier here
Rstructs [Raddress].data= data;
Rlist.push(Raddress);
Rstructs[Raddress].listpointer=Rlist.length -1;
return true;
}
function updateR (address Raddress, uint data) public returns(bool succes){
if (!isR(Raddress)) revert();
Rstructs[Raddress].data=data;
return true;
}
function deleteEntity(address Raddress) public returns (bool succes){
if(!isR(Raddress)) revert();
uint row2delete= Rstructs[Raddress].listpointer;
address toMove= Rlist[Rlist.length-1];
Rlist[row2delete]=toMove;
Rstructs[toMove].listpointer=row2delete;
Rlist.pop;
return true;
}
}
Hey everyone,
I pretty much understood everything in the âFinal Solutionâ part except for this code
delete entityStructs[entityAddress];
Itâs not part of the removal of the address in the array, right? But does it set that addressâ data to its Initial values?
If the address was deleted in the first place, why do we have to set its data to its Initial values? Or what is the reason behind doing this? I couldnât quite get it.
Thanks a lot, everyone!
Hey, @dan-i thank you for this knowledge. Does this mean that if data is set to its initial values (bool is false) it would not take space in storage? therefore no gas fees?
Also, I cannot deploy the contract it says
creation of mappedWithUnorderedIndexAndDelete errored: Cannot convert undefined or null to object
Thank you for the clarification.
Hi @CryptoXyz
Using the delete
method just sets the variable / string to its initial value:
A uint will be set to 0;
A boolean to false;
An address to 0x0 etcâŠ
Deploy this contract I wrote for you and check:
Call:
- set()
- get()
- delete()
- get()
you will see the results yourself.
pragma solidity 0.8.0;
contract test {
struct testStruct {
uint a;
bool b;
address c;
}
mapping (address => testStruct) public mappingOfTests;
function set () public {
mappingOfTests[msg.sender] = testStruct (10,true,msg.sender);
}
function get () public view returns (testStruct memory) {
return mappingOfTests[msg.sender];
}
function deleteData () public {
delete mappingOfTests[msg.sender];
}
}
Hey @dan-i,
Thank you so much for the effort of creating this contract to further answer my questions.
Hoping for your success!
Can you explain what the first line of code that incudes revert() does:
function NewEntity(address entityAddress, uint entityData) public returns(bool success){
if(isEntity(entityAddress)) revert();
entityStructs[entityAddress].entityData = entityData;
entityStructs[entityAddress].isEntity = true;
return true;
}
If the function isEntity(entityAddress)
returns true the transaction is reverted.
Of course. Calling the function isEntity. Thank you.
MODIFIED DELETE FUNCTION
Hey guys so i modified Filips delete function in the final mapping storage solution program to make it a little easier to read (its not technically better its just shortened) and it also consumes less gas. I just used his function which was actually similar to my deleteUser function in my multistig wallet sol.
//FILIPS SOLUTION
//this function allows us to delete entities.
function deleteEntity(address entityAddress) public returns(bool success) {
if(!isEntity(entityAddress)) revert();
uint rowToDelete = entityStructs[entityAddress].listPointer; // = 1
address keyToMove = entityList[entityList.length-1]; //save address4
entityList[rowToDelete] = keyToMove;
entityStructs[keyToMove].listPointer = rowToDelete; //= 2
entityList.pop();
delete entityStructs[entityAddress];
return true;
}
//UPDATED SOL
function removeEntity(address _entityAddress) public {
//require that the entity we wish to delet exists
if (!isEntity(_entityAddress)) revert();
uint entity_ID = entityStructs[_entityAddress].listPointer; //assign ID for deletion to entity instance ID
entityList[entity_ID] = entityList[entityList.length - 1];
entityList.pop();
delete entityStructs[_entityAddress];
}
the solution is very much the same in terms of style but i think its a little easier to follow. So instead of making a ârowToDeleteâ and âKeyToMoveâ variables i just create an entity ID which is assigmed to the ID of our entity instance (remember that this is already a prediefined attribute when we create an entity instance).
The to reiterate what Filip says, we cannot delete internal elements in a list we can only âpopâ off the last index. So we assign our entity_ID to the last index of the array but here its done in one line and is easier to read i think.
Lastly we do not need the last operation in filips code given by this line
entityStructs[keyToMove].listPointer = rowToDelete; //= 2
we can just simply pop straight away because we know that the Entity address that we want to delete is now stored in the last index. An finally just delete the struct as normal. From testing this shortened version seems to work just fine.
This solution is not technically better as the approach is the same but it seems to have a reduction in gas cost because there is less operations but the the percentage difference is only around 20% from the few tests i did. its just easier to read in my opinion for anyone who was struggling to follow Filips sol.
ARRAY STORAGE WITH DELETE FUNCTIONALITY
I was playing around with the simplearray solution from the first video to see if it could be modified to handle deletion. I havent went deep into testing or refining this solution i probably should write a few other functions to help me test but here it is anyway. It is deletion by index so may be this is not desirable but i wanted to play around with the array storage method some more
There is a lot of comments they are for my own sake the delete function is at the bottom
pragma solidity 0.8.0;
contract simpleList {
//define simple struct
struct EntityStruct {
address entityAddress;
uint entityData;
}
//create array to store data of struct instances (Log Array)
EntityStruct[] entityStructs;
mapping (address => bool) knownEntity;
//function to check for known entityStructs
function isEntity(address _entityAddress) public view returns(bool) {
return knownEntity[_entityAddress];
}
//create function to create struct instance and push it to the struct log array
function newEntity(address entityAddress, uint entityData) public returns(EntityStruct memory) {
//defines a new instance of our entity struct
if(isEntity(entityAddress)) revert();
EntityStruct memory newEntity;
//defines the attributes of the struct
newEntity.entityAddress = entityAddress;
newEntity.entityData = entityData;
//pushes this new entity instance to our Struct log array
entityStructs.push(newEntity);
knownEntity[entityAddress] = true;
//returns the new entity (-1 because array indexing starts at 0)
return entityStructs[entityStructs.length - 1];
}
//updates entity data
function updateEnti(uint _id, address _entityAddress, uint _entitydata) public {
if(!isEntity(_entityAddress)) revert();
if(entityStructs[_id].entityAddress != _entityAddress) revert();
entityStructs[_id].entityData = _entitydata;
}
//getter function that returs a certain id that the user inputs
//function that returns the size of our struct log array
function getEntityCount() public view returns(uint entityCount) {
return entityStructs.length;
}
//function to return our entoty struct log
function getEntityLog() public view returns (EntityStruct[] memory) {
return entityStructs;
}
//function to delete entty instance
function removeEntity(uint _id) public
{
//reuire that the entity log array is greater or equal than 1
require(entityStructs.length >= 1);
//incase we want to remove a user that is not at the end of the simpleList
//take the id of that entity and push it to the end of the list
entityStructs[_id] = entityStructs[entityStructs.length - 1];
address entityAddress = entityStructs[_id].entityAddress;
knownEntity[entityAddress] = false;
//then pop that user
entityStructs.pop();
//owners;
}
}
Filip & Co. I am hoping you can clarify on a question from the Simple Array Mapping lesson.
This is the 2nd lesson in the Storage Design Patterns.
In this lesson you explained the simple_array.sol file has some drawbacks one of those being that you cannot input your address to easily retrieve the entity you want as it does not contain a mapping. Instead you would have to create a loop function to iterate through all the addresses to find the entity and values you are looking for. You did however show us that we can return an ID associated with each new entity that is returned to u (return entityStructs[entityStructs.length - 1]; Since we are getting a unique ID back isnât it very easy to take that ID and get the address and other values in our entity?
Correct me if I am wrong but you would create a getFunction or something that would take this ID that has been returned to us, input it into our function and it will return the struct with the address and other info? So we donât even need to set up a loop just as long as we have the ID because the ID returned to us can be put into our function to pull up the address? Is that correct or am I am I missing something?