Push method for an array of structures - solidity

contract task_list {
struct tasksStruct {
string name;
uint32 timestamp;
bool is_done;
}
tasksStruct[] tasks;
function add_task(string _name) public {
tasksStruct newTask = tasksStruct(_name, now, false);
tasks.push(newTask);
}
function show_opened_tasks() public view returns (uint) {
uint count_of_opened_tasks = 0;
for (uint i=0; i<tasks.length; i++){
if (!tasks[i].is_done) {
count_of_opened_tasks += 1;
}
}
}
}
how does pushing for an array of structures work here? and why there is an error out of range of the array?

You're missing the data location for the reference types in the add_task() function. After fixing this, you'll be able to push to the array.
// added location `memory` for `name`
function add_task(string memory _name) public {
// added location `memory` for `newTask`
tasksStruct memory newTask = tasksStruct(_name, uint32(now), false);
tasks.push(newTask);
}
Also you probably want to return the count_of_opened_tasks from the show_opened_tasks() function - otherwise you'll always get returned the default value 0.
function show_opened_tasks() public view returns (uint) {
uint count_of_opened_tasks = 0;
for (uint i=0; i<tasks.length; i++){
if (!tasks[i].is_done) {
count_of_opened_tasks += 1;
}
}
// added return
return count_of_opened_tasks;
}

Related

How to store unique value in an array using for loop in Solidity?

I want to store unique addresses in the array and then increment the count. But I am not able to do it. The count variable is not incremented and returns 0. Also redundant addresses are stored in the array.
Can you please help?
function placeBid() public payable notOwner afterStart beforeEnd returns(bool){
require(auctionState == State.Running);
uint currentBid = 0;
if(totalBidder>0)
{
currentBid = bids[msg.sender] + msg.value;
require(currentBid > highestBindingBid);
}
else{
require(msg.value > startBid && msg.value > highestBindingBid);
currentBid = msg.value;
}
bids[msg.sender] = currentBid;
for(uint i=0; i<bidders.length; i++){
if(msg.sender!=bidders[i])
bidders.push(payable(msg.sender));
totalBidder++;
}
}
highestBindingBid = currentBid;
highestBidder = payable(msg.sender);
return true;
}
An easy (and cheaper compared to your snippet) way to push only unique items to a storage array is to duplicate the values in a mapping, and then validate against the mapping.
pragma solidity ^0.8;
contract MyContract {
address payable[] public bidders;
mapping (address => bool) isBidder; // default `false`
// only add `msg.sender` to `bidders` if it's not there yet
function placeBid() public {
// check against the mapping
if (isBidder[msg.sender] == false) {
// push the unique item to the array
bidders.push(payable(msg.sender));
// don't forget to set the mapping value as well
isBidder[msg.sender] = true;
}
}
}

Try to return an array in a for loop

I am very new to Solidity, so sorry for the silly question. I am trying to add an element of a struct and then return it, here is my code:
struct Flat {
uint256 priceInWei;
address currentOccupant;
bool flatIsAvailable;
}
Flat[8] public flatDB;
modifier landlordOnly() {
require(msg.sender == landlordAddress);
_;
}
constructor() public {
landlordAddress = msg.sender;
for (uint i=0; i<8; i++) {
flatDB[i].flatIsAvailable = true;
if (i % 2 == 0) {
flatDB[i].priceInWei = 0.1 ether;
} else {
flatDB[i].priceInWei = 0.2 ether;
}
}
}
uint256[] array;
function getFlatDB() payable public returns (uint256) {
for (uint i=0; i<8; i++) {
array.push(flatDB[i].priceInWei);
}
return array;
}
But when I try to compile I have an error at line 41:
TypeError: Return argument type uint256[] storage ref is not implicitly convertible to expected type (type of first return variable) uint256. return array; ^---^
Can someone explain me what's wrong? Thanks!
Seems like the return type you have (uint256) doesn't correspond to the type you're actually trying to return (uint256[]). Try rewriting your getFlatDB function in the following way:
function getFlatDB() payable public returns (uint256[]) {
uint256[] memory array = new uint256[](8);
for (uint i=0; i<8; i++) {
array[i] = flatDB[i].priceInWei;
}
return array;
}
Note how we declare the temporary fixed size return array inside the function with memory keyword.

Solidity: Returns filtered array of structs without 'push'

I have this contract with an array of structs:
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
contract Tickets {
struct Ticket {
uint id;
int val;
}
Ticket[] tickets;
function addTicket(uint id, int val) public returns(bool success) {
Ticket memory newTicket;
newTicket.id = id;
newTicket.val = val;
tickets.push(newTicket);
return true;
}
function getTicket(uint id) public view returns(Ticket memory) {
uint index;
for(uint i = 0; i<tickets.length; i++){
if (tickets[i].id == id) {
index = i;
break;
}
}
Ticket memory t = tickets[index];
return t;
}
function findTickets(int val) public view returns(Ticket[] memory) {
Ticket[] memory result;
for(uint i = 0; i<tickets.length; i++){
if (tickets[i].val == val) {
result.push(tickets[i]); // HERE IS THE ERROR
}
}
return result;
}
}
I need to returns a filtered by val array but when I buil this code: result.push(tickets[i].id); it throw this error:
TypeError: Member "push" is not available in struct Tickets.Ticket memory[] memory outside of storage.
How I can implement the filter without using push ?
Returning dynamic-length array of struct is still a bit tricky in Solidity (even in the current 0.8 version). So I made a little workaround to make it work even in the 0.6.
Determine the result count, you'll need it for step 2.
Create a fixed-length array
Fill the fixed-length array
Return the fixed-length array
function findTickets(int val) public view returns(Ticket[] memory) {
uint256 resultCount;
for (uint i = 0; i < tickets.length; i++) {
if (tickets[i].val == val) {
resultCount++; // step 1 - determine the result count
}
}
Ticket[] memory result = new Ticket[](resultCount); // step 2 - create the fixed-length array
uint256 j;
for (uint i = 0; i < tickets.length; i++) {
if (tickets[i].val == val) {
result[j] = tickets[i]; // step 3 - fill the array
j++;
}
}
return result; // step 4 - return
}

Solidity smart contract for lottery, but I can only give the prize for the first one. I want it to give it to all winners

Can't make it gives the prize for all winners, just the first one person that putted the right combination. I think there is a problem with findWinners function but I can't find it. If I make address A the good combination, then B with a not good one, and finally C with also a good one, it only gives the prize to A.
pragma solidity ^0.5.1;
contract Lottery {
Game[] ListGames;
// uint public nbGames = ListGames.length;
uint price = 100000;
uint[6] winnerGame;
// winnerGame = [1,2,3,4,5,6];
address payable[] ListWinners;
uint winnersPayed = 0;
uint expiredGames = 0;
struct Game {
address payable gamer;
uint[6] numbers;
uint date;
bool valid;
}
function setWinner(uint[6] memory _winnerGame) public {
winnerGame = _winnerGame;
}
function findWinners() public returns (address payable[] memory) {
uint size = ListGames.length;
bool win = true;
for (uint j = 0; j < size; j++) {
for (uint i=0; i<6 ; i++) {
if ((ListGames[j].numbers[i] != winnerGame[i]) || !ListGames[j].valid) {
win = false;
}
}
if (win) {
ListGames[j].valid = false;
expiredGames ++;
ListWinners.push(ListGames[j].gamer);
}
}
return ListWinners;
}
function payer() public returns (uint) {
uint nbWinners = ListWinners.length;
uint prize;
if (nbWinners - winnersPayed != 0) {
prize = address(this).balance / (nbWinners- winnersPayed);
for (uint i = 0; i < nbWinners; i++) {
if(ListWinners[i] != address(0)) {
ListWinners[i].transfer(prize);
delete ListWinners[i];
winnersPayed++;
require(ListWinners[i] == address(0));
}
}
}
return nbWinners;
}
modifier costs(uint amount) {
require(msg.value >= amount);
_;
}
function postGame(uint[6] memory _numbers) public payable costs(price) {
Game memory newGame = Game(msg.sender, _numbers, block. timestamp, true);
ListGames.push(newGame);
}
function numberValidGames() public view returns (uint) {
return ListGames.length - expiredGames;
}
function getWinnersList() public view returns (address payable [] memory) {
return ListWinners;
}
function getTime() public view returns (uint) {
return block.timestamp;
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}

Add element to struct

I can't use ( push ) because it is used with state variable only
This is the error :
Error message
Is there any alternatives for ( push )
contract m{
struct Message{
address sender;
address receiver;
uint msgContent;
} // end struct
Message[] all;
function get ( address from ) internal
returns ( Message[] subMsgs){
for ( uint i=0; i<all.length ; i++)
{
if ( all[i].sender == from )
{
subMsgs.push (all[i]);
}
}
return subMsgs;
}
} // end contract
You can only use push on dynamic-size arrays (i.e. storage arrays), and not fixed-size arrays (i.e. memory arrays) (see Solidity array documentation for more info).
So to achieve what you want, you'll need to create a memory array with a desired size and assign each element one by one. Here is the example code:
contract m {
struct Message{
address sender;
address receiver;
uint msgContent;
} // end struct
Message[] all;
function get(address from) internal returns (Message[]) {
// Note: this might create an array that has more elements than needed
// if you want to optimize this, you'll need to loop through the `all` array
// to find out how many elements from the sender, and then construct the subMsg
// with the correct size
Message[] memory subMsgs = new Message[](all.length);
uint count = 0;
for (uint i=0; i<all.length ; i++)
{
if (all[i].sender == from)
{
subMsgs[count] = all[i];
count++;
}
}
return subMsgs;
}
} // end contract