I'm writing a function that is supposed to receive an array of uint's and I want to make a requirement that none of the elements in the array be the same and that all of the elements in the array be part of the pre-selected elements to belong in an array. So far I have:
function vote(uint[] memory proposals) external
{
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote.");
require(!sender.voted, "Already voted.");
mapping(uint => bool) duplicateVotes;
require(!duplicateVotes, "Cannot vote for a proposal more than once.");
sender.voted = true;
sender.vote = proposals;
proposals[proposals].voteCount += sender.weight;
}
But I get an error:
The data location must be "storage", "memory" or "calldata" for variable, but none was given.
But I think this error is just a symptom of a larger problem. Can anyone help me figure out a solution to this problem?
I think u want to achieve that same person does not vote twice but the way you are doing is computationally expensive and this will cost too much gas. Instead, crate a mapping to keep track if voter has voted before:
mapping(address=>bool) peopleWhoVoted;
also you want to keep array of voters
address[] public listOfVoters;
So in your vote function, make sure that caller of this function-msg.sender is not in the peopleWhoVoted mapping
function vote(pass argument) public {
// msg.sender is globally available, it is function caller
require(!peopleWhoVoted[msg.sender],"this caller already voted")
// now u are sure that this caller did not vote before
// so u can add the function caller to the array
listOfVoters.push(msg.sender)
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
contract findDupli {
uint[] public arr = [2,3,4,8,1,7,8];
function funFind() public view returns(uint) {
uint temp;
for(uint i = 0; i < arr.length; i++) {
temp = arr[i];
for(uint j = 0; j < arr.length; j++) {
if((j != i) && (temp == arr[j])) {
return temp;
}
}
}
}
}
Related
I am coding a simple voting system where a 'chairperson' is defined...Chairperson will add candidates but the voter are going to register themselves(no role of chairperson in adding of voters, Chairperson is only going to add candidates). Now In code I made an array of voters of type structs and make a function named "vote" which is going to be called only by voter. So, I add a check of that the voter must be in the voterList(array of voter) or else voter must register first.. But when I call the function it always reverting it...even The voters are already registered....(I am going to add code of the contract below)...
[NOTE: THE PROBLEM IS IN ONE OF THE MODIFIER I CREATED NAMED "voterRules" IN THE CODE,I USED THIS MODIFIER IN THE VOTE FUNCTION...]
//SPDX-License-Identifier:UNLICENSED
pragma solidity ^0.8.0;
contract Ballot{
//VARIABLES
//Initialize an address of chairperson of voting system and set it private
address private chairperson;
//MODIFIERS
//To make chairperson as deployer
modifier isOwner(){
require(msg.sender == chairperson);
_;
}
//To make all the voters register by themselves
modifier isVoter(address voterAddress_){
require(voterAddress_ == msg.sender,"The voter must register by itself...");
_;
}
//To make rules for vote
modifier voterRules(){
require(msg.sender != chairperson,"Chairperson cannot vote!!");
for(uint m=0;m<voterList.length;m++){
// require(msg.sender == voterList[m]._voterAddress,"You must register yourself first...");
if( msg.sender != voterList[m]._voterAddress){
revert();
}
}
for(uint k=0;k<candidateList.length;k++){
require(voterToCandidate[msg.sender] != candidateList[k]._address,"You cannot vote twice...");
}
_;
}
//CONSTRUCTOR
//constructor to set deployer the chairperson
constructor(){
chairperson = msg.sender;
}
//STRUCTS
//Make a struct of candidate and give it properties: Name,Address,No. of votes
struct Candidate{
// string _name;
address _address;
uint256 _noOfVotes;
}
//Make a struct of voter and give it properties: Address,IsVoted
struct Voter{
address _voterAddress;
bool _isVoted;
}
//ARRAYS
//Make a array of candidates
Candidate[] public candidateList;
//Make an array of voters
Voter[] public voterList;
//MAPPINGS
//Mapping from address of Voter to stuct candidate
mapping(address=>address) private voterToCandidate;
//FUNCTIONS
//Make a function to add candidate in the mapping..
function addCandidate(address address_) public isOwner{
for(uint i=0;i<candidateList.length;i++){
require(address_ != candidateList[i]._address, "Same Candidate cannot be added twice");
}
Candidate memory candidate = Candidate(address_,0);
candidateList.push(candidate);
}
//Make a function to make voter to register himself..
function registerVoter(address voterAddress_) external isVoter(voterAddress_){
for(uint j=0;j<voterList.length;j++){
require(voterAddress_ != voterList[j]._voterAddress,"y");
}
voterList.push(Voter(voterAddress_,false));
}
//To make a function for voters to vote their desirable candidate and increase the count votes of the candidates
function vote(uint256 _candId) public voterRules() {
voterToCandidate[msg.sender] = candidateList[_candId]._address;
candidateList[_candId]._noOfVotes++;
}
}
I believe you are pretty new to the solidity, here are a couple of things to keep in mind.
We have to avoid loops as much as we can, to save gas.
Use mappings instead of arrays.
mapping can also maps to structs.
use indentation for better code readability.
Define state variables at the top.
and now to answer your question, I've made a couple of changes in the code that implements your intended logic as per my understanding. I am not explaining what I've done, You can use diffchecker.com to compare the code and think why I did what I did, it will give you a better understanding.
//SPDX-License-Identifier:UNLICENSED
pragma solidity ^0.8.0;
contract Ballot {
struct Candidate {
// string _name;
address _address;
uint256 _noOfVotes;
}
address private chairperson;
Candidate[] public candidateList;
mapping(address => bool) private isInVoterList;
mapping(address => address) private voterToCandidate;
modifier isOwner() {
require(msg.sender == chairperson);
_;
}
modifier isVoter(address voterAddress_) {
require(
voterAddress_ == msg.sender,
"The voter must register by itself..."
);
_;
}
modifier voterRules() {
require(msg.sender != chairperson, "Chairperson cannot vote!!");
require(isInVoterList[msg.sender], "You must register yourself first");
for (uint256 k = 0; k < candidateList.length; k++) {
require(
voterToCandidate[msg.sender] != candidateList[k]._address,
"You cannot vote twice..."
);
}
_;
}
constructor() {
chairperson = msg.sender;
}
function addCandidate(address address_) public isOwner {
for (uint256 i = 0; i < candidateList.length; i++) {
require(
address_ != candidateList[i]._address,
"Same Candidate cannot be added twice"
);
}
Candidate memory candidate = Candidate(address_, 0);
candidateList.push(candidate);
}
function registerVoter(address voterAddress_)
external
isVoter(voterAddress_)
{
require(
isInVoterList[voterAddress_] == false,
"You are already registerd voter"
);
isInVoterList[voterAddress_] = true;
}
function vote(uint256 _candId) public voterRules {
voterToCandidate[msg.sender] = candidateList[_candId]._address;
candidateList[_candId]._noOfVotes++;
}
}
suggesting watch
Mapping by Smart Contract Programmer
Iterable Mapping by Smart Contract Programmer
ps. dun forget to accept my answer.
I have a large contract and I am in the process of splitting it out into two. The goal is to have the functions that are common (and will be used by many other contracts) to be separated out for efficiency.
One of these functions compares items in arrays "ownedSymbols" and "targetAssets". It produces a list "sellSymbols" if any item in "ownedSymbols" is not in "targetAssets".
The below code works fine while "sellSymbols" is stored as a string. As this function will become common, I need it to run in memory so the results aren't confused by calls from different contracts.
pragma solidity >0.8.0;
contract compareArrays {
string[] public ownedSymbols = ["A","B","C"];
string[] public targetAssets = ["A","B"];
string[] sellSymbols;
event sellListEvent(string[]);
function sellList(string[] memory _ownedSymbols, string[] memory _targetAssetsList) internal {
sellSymbols = _ownedSymbols;
for (uint256 i = 0; i < _targetAssetsList.length; i++) {
for (uint256 x = 0; x < sellSymbols.length; x++) {
if (
keccak256(abi.encodePacked((sellSymbols[x]))) ==
keccak256(abi.encodePacked((_targetAssetsList[i])))
) {
if (x < sellSymbols.length) {
sellSymbols[x] = sellSymbols[sellSymbols.length - 1];
sellSymbols.pop();
} else {
delete sellSymbols;
}
}
}
}
emit sellListEvent(sellSymbols);
}
function runSellList() public {
sellList(ownedSymbols,targetAssets);
}
}
Ideally the function would run with "string[] memory sellSymbols", however this kicks back an error.
pragma solidity >0.8.0;
contract compareArrays {
string[] public ownedSymbols = ["A","B","C"];
string[] public targetAssets = ["A","B"];
event sellListEvent(string[]);
function sellList(string[] memory _ownedSymbols, string[] memory _targetAssetsList) internal {
string[] memory sellSymbols = _ownedSymbols;
for (uint256 i = 0; i < _targetAssetsList.length; i++) {
for (uint256 x = 0; x < sellSymbols.length; x++) {
if (
keccak256(abi.encodePacked((sellSymbols[x]))) ==
keccak256(abi.encodePacked((_targetAssetsList[i])))
) {
if (x < sellSymbols.length) {
sellSymbols[x] = sellSymbols[sellSymbols.length - 1];
sellSymbols.pop();
} else {
delete sellSymbols;
}
}
}
}
emit sellListEvent(sellSymbols);
}
function runSellList() public {
sellList(ownedSymbols,targetAssets);
}
}
The error:
TypeError: Member "pop" is not available in string memory[] memory outside of storage.
--> contracts/sellSymbols.sol:20:25:
|
20 | sellSymbols.pop();
| ^^^^^^^^^^^^^^^
Two questions from me:
Is there a way to do this in memory so that the function can be common (i.e. used by multiple contracts at the same time)?
Is there a better way? The below is expensive to run, but it is the only way I have been able to achieve it.
One final comment - I know this would be much easier/cheaper to run off chain. That is not something I am willing to consider as I want this project to be decentralized.
If you want to keep the existing system, the best solution is described here: https://stackoverflow.com/a/49054593/11628256
if (x < sellSymbols.length) {
sellSymbols[x] = sellSymbols[sellSymbols.length - 1];
delete sellSymbols[myArray.length - 1];
sellSymbols.length--;
} else {
delete sellSymbols;
}
If all you care about is the presence or absence of a particular asset (and not enumerating through them), what you're going to want to do to really reduce gas costs is something called "lazy evaluation." Lazy evaluation is when instead of computing all results at once (like increasing all balances by 50% by iterating over an array), you modify the getters so that their return value reflects the operation (such as multiplying an internal variable by 50% and multiplying the original result of getBalance by that variable).
So, if this is the case, what you want to do is use the following function instead:
function except(string _item, mapping(string => bool) _ownedSymbols, mapping(string => bool) _targetAssets) internal returns (bool) {
return _ownedSymbols[_item] && !_targetAssets[_item];
}
<pet peeve>
Finally, I know you say you want this to be decentralized, but I really do feel the urge to say this. If this is a system that doesn't need to be decentralized, don't decentralize it! Decentralization is great for projects that other people rely on - for example, DNS or any sort of token.
From your variable names, it seems that this is probably some sort of system similar to a trading bot. Therefore, the incentive is on you to keep it running, as you are the one that gets the benefits. None of the problems that decentralization solves (censorship, conflict of interest, etc...) apply to your program, as the person running it is incentivized to keep it running and keep a copy of the program. It's cheaper for the user running it to not have security they don't need. You don't need a bank-grade vault to store a $1 bill!
</pet peeve>
I have the following function in Solidity which takes as arguments 2 arrays, an array of shareholder addresses and an array of their stakes. I'm keeping an array of shareholders in storage, together with a map to their stakes.
If the updated array is the same size, it's simple, just overwrite each position with the new values. If they are different sizes however, I first go through the entire array and delete each element, and then insert the new ones. I feel this is not very efficient and it could be done better.
PS: I am a complete beginner to Solidity and this is my first contract, so please feel free to let me know if I'm doing anything stupid or inefficiently.
Thanks !
function setShareholders(address[] _shareholders, uint256[] _stakes) public onlyCEO {
require(_shareholders.length > 0);
require(_shareholders.length == _stakes.length);
uint256 accummulator = 0;
for(uint8 x = 0; x < _stakes.length; x++){
require(_addressNotNull(_shareholders[x]));
require(_stakes[x] > 0 && _stakes[x] <= 100);
accummulator = SafeMath.add(accummulator, _stakes[x]);
}
if(accummulator == 100){ // stakes need to add up to 100%
_setShareholders(_shareholders, _stakes);
}
}
function _setShareholders(address[] _shareholders, uint256[] _stakes) private {
if(_shareholders.length == shareholders.length){
for(uint8 x = 0; x < shareholders.length; x++) {
shareholders[x] = _shareholders[x];
shareholderToStake[_shareholders[x]] = _stakes[x];
}
}
else {
for(x = 0; x < shareholders.length; x++) {
delete shareholders[x];
shareholders.length--;
delete shareholderToStake[shareholders[x]];
}
for(x = 0; x < _shareholders.length; x++) {
shareholders.push(_shareholders[x]);
shareholderToStake[_shareholders[x]] = _stakes[x];
}
}
}
In theory, this would work...unfortunately in solidity, managing arrays is a costly nightmare. Doing any array manipulation, on not just one but 2 arrays, is not recommended at all.
You could keep your array of shareholders...but from there, I'd recommend creating a mapping of address->structure. This way you can loop through your mapped structure and contain all necessary data within that.
So something like this for your refactor:
address[] public shareholders;
struct ShareHolder {
uint stake;
// ...other awesome data here
}
mapping (address => ShareHolder) public shareholderData;
This way, you have your shareholders list. And you can directly access a shareholder with shareholderData[<SHAREHOLDER ADDRESS>].
I have a boolean free defined as
bool inbetween, free, lunch;
The only other times I use the boolean are here
//get time info
memset(period, 0, sizeof(period));
free = false;
inbetween = false;
lunch = false;
(I declare it true in a couple of if statements)
And here:
if(free){
for(int i=0;i<=3;i++){
period[i] = FREE[i];
}
}
if(lunch){
for(int i=0;i<=4;i++){
period[i] = Lunch[i];
}
}
if(inbetween){
for(int i=0;i<=8;i++){
period[i] = Inbetween[i];
}
}
I use all 3 booleans the same amount of times, yet I'm only getting this error with free
free is a library function that is used to free memory. I recommend that you not use free as a variable name.
edit: I can't believe I didn't catch this sooner. Turns out my problem was re-declaring my first variables over and over again, essentially starting the program fresh instead of continuing it. To fix it, I replaced the first two lines with this:
if (initialized === undefined) {
trace("INITIALIZING");
var MCs = [];
var lastPos = "intializer";
var initialized = 1;
}
Now it works like a charm. I feel like a noob for this one; sorry to anyone whose time I wasted. I'd post this as an answer to my own question, but it won't let me since I'm still new.
Original Post follows:
I'm trying to make a flash that will randomly choose an ad, play it, and then randomly play another. To that end, I've succeeded by shuffling an array, and then gotoAndPlay-ing the label in the first element of the array, and then removing that element. At the end of each ad is gotoAndPlay(1); with all the main code being on the first frame. If the array is empty, it rebuilds it and reshuffles it.
The problem is, I don't want it to repeat any ads until its run through all of them; I think I've got that down, but I'm not positive. Further, I don't want the last element in the array to be the same as the first in the new one, so the same ad won't ever show twice in a row. I'm trying to have it detect if the element it just used matches the one it's about to use, and reshuffle if that happens, but in my testing it continues to occasionally show the same ad twice in a row.
I'm obviously doing something wrong, but being entirely new to ActionScript3 (and in fact to flash) I'm having a lot of trouble identifying what it is. Here's what I have right now:
var MCs = [];
var lastPos = "intializer";
if (MCs.length == 0) {
MCs = reset();
if (lastPos == MCs[0]) {
while (lastPos == MCs[0]) {
MCs = reset();
}
}
}
if (MCs.length > 0) {
lastPos = MCs[0];
MCs.splice(0,1);
gotoAndPlay(lastPos+"MC");
}
function reset(){
var PrepMCs = new Array("Image1", "Image2", "Image3");
var WorkMCs = new Array(PrepMCs.length);
var randomPos:Number = 0;
for (var i:int = 0; i < WorkMCs.length; i++)
{
randomPos = int(Math.random() * PrepMCs.length);
WorkMCs[i] = PrepMCs.splice(randomPos, 1)[0];
}
return WorkMCs;
}
Personally, I'd rather just do this with JavaScript, HTML, and images; it'd be really simple. But for hosting/CMS reasons I don't have any control over, I'm limited to a single file or a single block of code; I can't host anything externally, which as far as I can tell leaves Flash as my best option for this.
Any help would be greatly appreciated, thanks! If I've done something horribly, horribly wrong, and it's a wonder this even runs at all, don't hesitate to tell me!
edit: It just occurred to me, it is perfectly fine if the second run is in the same order as the first run, etc. The main thing is, it needs to be random. This is probably much easier to implement.
edit 2: MASSIVE DERP HERE. Every time it runs, it re-initializes MCs and lastPos... in other words, it's shuffling every time and starting over. What I should be researching is how to only run a line of code if a variable hasn't been initialized yet.
Blatantly stealing from #32bitKid, this is my version.
The main problem I have with his solution is the push/splice idea. As much as possible, I like to create once, and reuse. Shrinking and growing arrays is bulky, even if effective.
Also, this method does not re-order the array, which may or may not be valuable.
BTW, I like the way that he prevents a repeat of the previous item ("almost empty").
So here is another method:
package
{
public class RandomizedList
{
private var _items:Array;
private var idxs:Array;
private var rnd:int;
private var priorItemIdx:int;
private var curIdx:int;
public function RandomizedList(inarr:Array)
{
items = inarr;
}
private function initRandomize():void
{
idxs = new Array();
//Fisher-Yates initialization (http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle):
idxs[i] = 0;
for (var i:int = 1; i < items.length; i++)
{
rnd = int(Math.random() * (i + 1));
idxs[i] = idxs[rnd];
idxs[rnd] = rnd;
}
curIdx = 0;
priorItemIdx = -1;
}
private function randomize():void
{
var tempint:int;
//Fisher-Yates (http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle):
for (var i:int = items.length; i >= 1; i--)
{
rnd = int(Math.random() * (i + 1));
tempint = idxs[i];
idxs[i] = idxs[rnd];
idxs[rnd] = tempint;
}
curIdx = 0;
}
public function next():void
{
if (curIdx >= idxs.length)
{
randomize();
}
if (items.length > 1 && priorItemIdx == idxs[curIdx])
{
curIdx++;
}
priorItemIdx = idxs[curIdx++];
return items[priorItemIdx];
}
public function get items():Array
{
return _items;
}
public function set items(value:Array):void
{
_items = value;
initRandomize();
}
}
}
I would use a utility class like this to abstract out the behavior I wanted:
import flash.text.TextField;
class Randomizer {
private var unused:Array = [];
private var used:Array;
public function Randomizer(playList:Array) {
used = playList;
}
public function next():* {
// If almost empty, refill the unused array
if(unused.length <= 1) refill();
// Get the first item off the playList
var item:* = unused.shift();
// Shove it into the bucket
used.push(item);
// return it back
return item;
}
public function refill():void {
var i:int;
// Fisher-Yates shuffle to refill the unused array
while(used.length > 0) {
i = Math.floor(Math.random() * used.length)
unused.push(used.splice(i,1)[0])
}
}
}
Notice that it refills the unused array when the unused array still has one item in it, this makes it impossible for the last result to repeat twice in a row. This will return each item once before before looping, and will never repeat the same item twice.
You would use it by saying something like:
var ads:Randomizer = new Randomizer(["Image1", "Image2", "Image3"]);
ads.next(); // will return something
ads.next(); // will return something
ads.next(); // will return something
ads.next(); // will return something
// Keep going into infinity...
There is a little test example of this code working here.
See if this makes any sense
//create your array of all your ad names/frame labels
var PrepMCs:Array = new Array("Image1", "Image2", "Image3");
var shuffledMCs:Array = [];
//store the name of the last played ad in this var
var lastAdPlayed:String;
//shuffle the array
shuffleArray(PrepMCs);
function shuffleArray(arrayToShuffle:Array):void {
//clear the array
shuffledMCs = [];
var len:int = arrayToShuffle.length;
for(var i:int = 0; i<len; i++) {
shuffledMCs[i] = arrayToShuffle.splice(int(Math.random() * (len - i)), 1)[0];
}
//test to see if the new first ad is the same as the last played ad
if (lastAdPlayed == shuffledMCs[0]) {
//reshuffle
shuffleArray(PrepMCs);
} else {
lastAdPlayed = [0];
trace(shuffledMCs);
playAds();
}
}
//after each ad has played, call this function
function playAds():void {
if (shuffledMCs.length > 0) {
gotoAndPlay(shuffledMCs[0]);
shuffledMCs.splice(0,1);
} else {
//array is empty so we have played all the ads
shuffleArray(PrepMCs);
}
}