find and remove element from array (solidity) - solidity

I've tackled a task: find a specific address in a sheet, move it to the end of the sheet, and remove it via a function pop! here is the code:
function removeAccount(address _account) external{
uint counter = arrayOfAccounts.length;
uint index;
for(uint i; i < counter; i++) {
if(arrayOfAccounts[i] == _account){
index = i;
break;
}
for(uint i = index; i < counter-1; i++){
arrayOfAccounts[i] = arrayOfAccounts[i + 1];
}
arrayOfAccounts.pop();
}
}
}
}
transact to Wote.removeAccount errored: VM error: revert.
revert
The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance.
Debug the transaction to get more information.

In case you dont care about addresses order
function removeAccount(address _account) external {
if(arrayOfAccounts.length == 1) {
arrayOfAccounts.pop();
}
else if(arrayOfAccounts[arrayOfAccounts.length - 1] == _account) {
arrayOfAccounts.pop();
}
else {
for (uint i = 0; i < arrayOfAccounts.length - 1; i++) {
if(_account == arrayOfAccounts[i]) {
arrayOfAccounts[i] = arrayOfAccounts[arrayOfAccounts.length - 1];
arrayOfAccounts.pop();
}
}
}
}

If order matters
function removeAccount(address _account) external{
uint counter = arrayOfAccounts.length;
for(uint i; i < counter; i++) {
if(arrayOfAccounts[i] == _account){
for(uint j = i; j < counter-1; j++){
arrayOfAccounts[j] = arrayOfAccounts[j + 1];
}
arrayOfAccounts.pop();
break;
}
}
}
}
Else if order doesn't matter
function removeAccount(address _account) external{
uint numAccounts = arrayOfAccounts.length;
for(uint i = 0; i < numAccounts; i++) {
if(arrayOfAccounts[i] == _account){ // if _account is in the array
arrayOfAccounts[i] = arrayOfAccounts[numAccounts - 1]; // move the last account to _account's index
arrayOfAccounts.pop(); // remove the last account
break;
}
}
}

The reason is just simple.
You used the second for loop inside the first for loop.
And also please initialize the index with counter;
uint256 index = counter;
And pop only when index is less than counter

Related

How can I avoid looping through this array twice?

My code is as follows:
/**
*
* #notice Returns a Sale array with all the sales that have not ended.
*
*/
function getOngoingSales() public view returns(Sale[] memory) {
uint256 _ongoingSalesCounter = 0;
for(uint i = 0; i<sales.length; i++) {
if (sales[i].ended == false) _ongoingSalesCounter++;
}
Sale[] memory _ongoingSales = new Sale[](_ongoingSalesCounter);
uint256 _pos = 0;
for(uint i = 0; i<sales.length; i++) {
if (sales[i].ended == false) {
_ongoingSales[_pos] = sales[i];
_pos ++;
}
}
return _ongoingSales;
}
The problem is that I have to loop twice the array to get to my wanted result. Is there a more effective way of doing this?
You can try to combine the two loops into one,
/**
*
* #notice Returns a Sale array with all the sales that have not ended.
*
*/
function getOngoingSales() public view returns(Sale[] memory) {
Sale[] memory _ongoingSales;
for(uint i = 0; i<sales.length; i++) {
if (sales[i].ended == false) _ongoingSales.push(sales[i]);
}
return _ongoingSales;
}
Before time complexity: O(2n)
After time complexity: O(n)

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
}

How I can sort an ArrayList of int arrays?

I have this code. I'm dealing with the N-Queen problem.
The problem is when I wanna show results by screen, the arrays are not ordered. But in this code I can't order them using Comparator. It's very strange because in other Class it works perfectly using Comparator, but here it doesn't work. Hope anyone could help me. Thanks in advance.
import java.util.*;
public class NReinas {
public static void resolverReinas(int n){
String[][] tablero;
tablero = generarTablero(n);
ubicarReina(tablero, 0, n);
}
public static void ubicarReina(String[][] tablero, int etapa, int n){
ArrayList <int[]> resultados = new ArrayList<>();
for(int i = 0; i < tablero.length; i++){
if(isValido(tablero, i, etapa)){
tablero[i][etapa] = "R";
if(etapa < tablero.length - 1){
ubicarReina(tablero, etapa + 1, n); //Recursividad
}else {
resultados.add(devolverSolucion(tablero, n));
}
tablero[i][etapa] = " "; //Backtracking: vaciamos el tablero
}
}
//The ArrayList I want to order by int arrays
for (int[] r : resultados) {
System.out.println(Arrays.toString(r));
}
}
public static boolean isValido(String[][] tablero, int i, int etapa){
for(int x = 0; x < etapa; x++){
if(tablero[i][x].equals("R")){
return false;
}
}
for(int j = 0; j < tablero.length && (i-j) >= 0 && (etapa-j) >=0; j++){
if(tablero[i - j][etapa - j].equals("R")){
return false;
}
}
for(int j = 0; j < tablero.length && (i + j) < tablero.length && etapa - j >= 0; j++){
if(tablero[i + j][etapa - j].equals("R")){
return false;
}
}
return true;
}
public static String[][] generarTablero(int length){
String[][]res = new String[length][length];
for (int i = 0; i < res.length; i++) {
for (int j = 0; j < res.length; j++) {
res[i][j] = " ";
}
}
return res;
}
public static int[] devolverSolucion(String[][] tablero, int n){
int[] solucion = new int[n];
for (int i = 0; i < tablero.length; i++) {
for (int j = 0; j < tablero.length; j++) {
if(tablero[i][j] == "R"){
solucion[i] = j;
}
}
}
return solucion;
}
}
Try Using Integer instead of int and save array values on List instead, so you can use sort them
List<Integer> list = Arrays.asList(solucion);
Collections.sort(list);
If you insist in using and array you can reconverti the list to an array
(Integer[]) list.toArray();

Getting an invalid vm error opcode in solidity, any ideas?

I am trying to test this simple contract to remove an address from an array but I keep getting this "invalid opcode" error.
contract C {
address[] addrList;
function addAddr(address addr) public{
addrList.push(addr);
}
function deleteAddr(address addr)public {
for(uint256 i = 0; i < addrList.length; i++) {
if(addr == addrList[i]) {
for(uint256 j = i; j < addrList.length; j++)
addrList[j] = addrList[j + 1];
delete addrList[addrList.length - 1];
addrList.length--; // <== This gives Error: Expression has to be an lvalue.
}
The error is coming because you are iterating variable j from i to addrList.length-1. This makes the addrList[j+1] out of index.
You need to iterate j from i to addrList.length-2. This can be achieved by updating the condition to j < addrList.length-1.
pragma solidity ^0.5.3;
contract C {
address[] addrList;
function addAddr(address addr) public{
addrList.push(addr);
}
function deleteAddr(address addr) public {
for(uint256 i = 0; i < addrList.length; i++) {
if(addr == addrList[i]) {
for(uint256 j = i; j < addrList.length-1; j++) {
addrList[j] = addrList[j + 1];
}
//delete addrList[addrList.length - 1];
addrList.length--;
}
}
}
function returnList() view public returns (address[] memory) {
return addrList;
}
}
Also, you do not require to use the delete step as compiler cleans up the memory slots by itself.

ArrayList Method Returns a null ArrayList, main program cannot access

I'm trying to create a basic function that calls on a method that creates the 2D ArrayList that will be used further in the main program to do things like calculate the row and column sums as well as print out the triangle.
However, after it runs the ArrayList returns null. What's going on?
Thanks,
public class Trib
{
private ArrayList<ArrayList<Integer>> triangle;
private int Asize;
public Trib (int size)
{
// convert the argument to type 'int' to be used in the program
Asize = size;
// create an ArrayList of ArrayLists, it will have 'size' number ArrayLists contained within
ArrayList<ArrayList<Integer>> triangle = new ArrayList<ArrayList<Integer>>(size);
// create the inner ArrayLists
for (int i = 0; i < size; i++)
{
// add to index 'i' of our ArrayList a new ArrayList of size (i+1)
triangle.add(new ArrayList<Integer>(i+1));
for (int j = 0; j <= i; j++)
{
if (j==0 || j == i)
{
triangle.get(i).add(1);
}
else
triangle.get(i).add(triangle.get(i-1).get(j-1)+triangle.get(i-1).get(j));
System.out.print(triangle.get(i).get(j) + " ");
}
System.out.println();
}
triangle.clone();
}
public void printTriangle()
{
System.out.print(triangle.get(1).get(1));
/*for (int i = 0; i < Asize; i++)
{
for (int j = 0; j <= i; j++)
{
System.out.print(triangle.get(1).get(1) + " ");
}
System.out.println();
}*/
}
/*public Trib()
{
this(5);
}*/
/*public int Psize()
{
return triangle.size();
}
public ArrayList<Integer> sumRows()
{
ArrayList<Integer> row_sum = new ArrayList<Integer>(Asize);
for (int i = 0; i < Asize; i++)
{
for (int j = 0; j < i; j++)
{
row_sum.add(triangle.get(i).get(j));
}
}
return row_sum;
}
public ArrayList<Integer> sumCols()
{
ArrayList<Integer> col_sum = new ArrayList<Integer>(Asize);
for (int i = 0; i < Asize; i++)
{
for (int j = 0; j < i; j++)
{
col_sum.add(triangle.get(i).get(i));
}
}
return col_sum;
}*/
public static void main(String[] args)
{
if(args.length < 1)
{
System.err.println("Sorry, this program needs an integer argument.");
System.exit(1);
}
Trib pt = new Trib(Integer.parseInt(args[0]));
pt.printTriangle();
//ArrayList<Object> sum_rows = new ArrayList<Object>(pt.Psize());
// sum_rows.add;
System.out.println("\nHere are the sum of rows:");
//for (int i = 0; i < pt.Psize(); i++)
//System.out.println(sum_rows.get(i));
//ArrayList<Integer> sum_cols = new ArrayList<Integer>(pt.Psize());
System.out.println("\nHere are the sum of columns:");
//for (int i = 0; i < pt.Psize(); i++)
//System.out.printf("%-5d", sum_cols.get(i));
}
}
Watch out what's what you are doing: Notice that you have TWO variables named "triangle": The first one is an instance variable and the second is a local variable, which is the only one you have initialized.
My suggestion to avoid this common mistake is to pre-pend "this." to any use of what you intend must be an instance variable. And, if in doubt, if you use a development environment as Eclipse, press CTRL and click on your variable to navigate to the point where it is declared.