Based on my Debug observation:
I have tested that:
- Firstly I insert data into my map data-structure as I explained in my question.
- Secondly, I have returned the address of first mapped index, which is 0, this could be done for other indexes as well (0:10).
- Without a delete operation I can retrieve the data perfectly fine from the address of mapped Foo[]. But if I did the delete operation, Solidity gives an error.
This debug result: leads me to conclude that memory is freed or address does not contain any valid information.
Test code:
set_txn_hash = my_contract.transact().map_insert();
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
for x in range(0, 9):
for y in range(0, 9):
output = my_contract.call().get_(x, y);
print(output); //returns correct value.
set_txn_hash = my_contract.transact().map_remove_map();
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
set_txn_hash = my_contract.transact().get_map_address(0);
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
output = my_contract.call().get_map();
print(output);
output = my_contract.call().try_();
print(output); //Solidity gives error if delete map_remove_map() is called
Additional functions inside my Contract:
function map_insert(){
for(uint i = 0; i < 10; i++)
for(uint j = 0; j < 10; j++)
add(i, i*10 + j);
}
function map_remove_map(){
for(uint i = 0; i < 10; i++)
delete_(i);
}
function map_remove_map_item(){
for(uint i = 0; i < 10; i++)
for(uint j = 0; j < 10; j++)
delete_items(i, j);
}
function get_map_address(uint id) {
uint addr;
Foo[] a = foo[id];
assembly{
addr := a
}
map_address = addr;
}
function get_map() constant returns (uint){
return map_address;
}
function try_() constant returns (uint ){
uint addr = map_address;
Foo[] a;
assembly{
a := addr
}
return a[1].x;
}
This is a fairly high level of abstraction because I don't know exactly what the application is going to do or how you will structure the deployment. Three-part answer.
First, I would be leery about a loopy process. Try to avoid whenever possible and keep it well-bounded when it's unavoidable. The rationale includes the fact that estimating gas may be difficult, and at scale, it's possible the process won't be able to run at all due to the block gas limit.
It's often possible to get around that sort of thing by exposing a single step ... // or do something else ... as a function. You rely on something outside the contract, i.e. a node server or a UI client, etc., to iterate over a loop and call that function as often as necessary.
Second part. Iterating over a mapping itself isn't possible but something like that is often needed. A solution is to maintain an index and iterate over that.
address[] clusterContractIndex;
Mulling over how to present an example, I started developing a feeling that mapping by the block number isn't the way to go, so I've changed to mapping by address. There won't be any collision if multiples are created in the same block with this change. You may want to add the block number to the struct to record it somewhere as you work out the details. Note that this won't be necessary to get contracts created in a certain block.
As a general habit, consider the usefulness of some functions to make the mapping iterable:
function getContractCount() returns(uint count) {
return clusterContractIndex.length;
}
function getContractAtIndex(uint row) returns(address contract) {
return clusterContractIndex[row];
}
Inside the function that records them, write the structs to the mapping, and also push()
the keys to the index:
function newClusterContract(address newContract) {
clusterStructs[newContract] = contractStruct;
clusterContractIndex.push(newContract);
This is a cumulative add-only process. Now you can get the contracts in the order they were created, or by their address directly with:
function getContract(address contract) returns(contract details) {}
Third. Efficient search and filtering. I don't have a good on-chain search process, yet :-)
In practice, you may be able to eject that requirement and depend on offchain services such as a database or even an in-memory sort by a browser UI (depending on expected scale). The takeaway here is most things that look like the need for an onchain index/search/filter solution are really better handled by offchain processes.
Add an event emitter to the contract whenever state changes.
event LogNewContract(address contract);
And when it happens:
LogNewContract(address newContract);
When you get on to building the UI, you'll probably find that a watcher over this can keep a synced copy of the on-chain truths, quickly sort and filter things, and then fetch the details of a contract that's known to exist. From the sound of it, it only needs to finds any contracts that actually exist in a given range of block numbers. Clients get the blocknumber a contract was created in when the event arrives. It's a bonus piece of every event received.
Hope it helps.
p.s. You can think of the mapping as a table in which all possible addresses exist and all remaining columns are reliably initialized to 0. If you pull a struct from an "empty" address you never wrote to, then all fields in the struct will be all 0. 0, false, 0x0, empty string, depending on type.
UPDATE
I've created an Order Statistics Tree that resolves a number of problems such as finding the median or rank of a value in a sorted list while also providing a method to cap gas cost to an absolute maximum/worst-case limit at any scale.
This repo builds on Bokky Poobah's Red Black Tree which provides the basis for the self-balancing tree. https://github.com/rob-Hitchens/OrderStatisticsTree
Best Answer
there is some mistakes (or i didn't understand well your question) in your previous code and i think also in your question.
in your case you have a map of arrays. so
Map[key].length
exists and return the size of the corresponding array to your key. soMap[msg.sender].length
is correct butMap[msg.sender][0].length
is not. because in this second case you are returning the first struct which doesn't have a length parameter but amy_length
.