I have a multidimensional array that I'm using as a box, and I have code that generates a border around it, like this:
#######
# #
# #
# #
# #
#######
However what I don't understand is that I can have either a 0 or a 1 in the "j == ProcArea.GetUpperBound(...)" part and it works successfully without any errors or unexpected output.
int[,] ProcArea = new int[rows, columns];
//Generate border
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
if (i == 0 || j == 0 || i == ProcArea.GetUpperBound(0) || j == ProcArea.GetUpperBound(1))
{
ProcArea[i, j] = 2;
}
}
}
Why does this work, and what is the correct value I should be using?
Thanks
If the number of rows and columns are the same, then GetUpperBound(0) and GetUpperBound(1) are going to return the same value.
Arrays you create in C# (unless you call Array.CreateInstance directly) are always 0-based. So GetUpperBound(0) will always return rows - 1, and GetUpperBound(1) will always return columns - 1.
So the code will "work" regardless of which upper bound you check, although I think you'll find that if rows != columns, then using GetUpperBound(0) will create a different sized box than GetUpperBound(1).
By the way, an alternate way of making your border would be:
var maxRow = ProcArea.GetUpperBound(0);
var maxCol = ProcArea.GetUpperBound(1);
// do top and bottom
for (int col = 0; col <= maxCol; ++col)
{
ProcArea[0, col] = 2;
ProcArea[maxRow, col] = 2;
}
// do left and right
for (int row = 0; row <= maxRow; ++row)
{
ProcArea[row, 0] = 2;
ProcArea[row, maxCol] = 2;
}
It's slightly more code, true, but you don't waste time checking indexes unnecessarily. Won't make a difference with small arrays, of course.
Check the documentation http://msdn.microsoft.com/en-us/library/system.array.getupperbound.aspx. Your array has 2 dimensions (rows and columns).
ProcArea.GetUpperBound(0) is equivalent to rows - 1
ProcArea.GetUpperBound(1) is equivalent to columns - 1
Related
I have set up a spreadsheet program with a 26 by 26 grid of cell objects. I set up a method which processes a formula entered by the user. The user can enter, for example, =A1+A2 and the method checks the values of cells A1 and A2 and adds them together. For some reason, the value of the second cell is not being assigned to the array.
https://pasteboard.co/IqRO23M.png
The code continues the switch case statement for other operators but that's not important for this problem.
public static void ProcessBasicFormula(Cell selectedCell,Cell[,]
cell,string[] cellsInUse, char operatorInUse)
{
int[] valueOfCell = new int[cellsInUse.Length]; //valueOfCell and
cellInUse have same length
int counter = 0;
double answer = 0.0;
switch(operatorInUse)
{
case ('+'):
for (int i = 0; i < cellsInUse.Length; i++)
{
for (int j = 0; j < cellsInUse.GetLength(0); j++)
{
for (int k = 0; k < cellsInUse.GetLength(0); k++)
{
if (cellsInUse[i] == cell[j, k].CellID)
{
valueOfCell[counter] =Convert.ToInt32(cell[j,k].Text);
counter++;
}
}
}
}
answer = valueOfCell[0] + valueOfCell[1];
break;
I expect the values of the two cells to be added together but instead, I am getting the first value added by 0.
my assumptions are:
the cellsInUse array length is accurate
List item the cell value at index 1 is not zero
the new int[int] will initialize the array to zeros;
int[] valueOfCell = new int[cellsInUse.Length];
cellsInUse is one dimensional array;
string[] cellsInUse
the getLength(0) of one dimensional array is the same as .length
cellsInUse.length == 2 is true;
cellsInUse.GetLength(0) == 2 is true;
so the j and k loops are looping to 2.
I am assuming the first cell value was lucky enough to be in first subset of [2,2] cells.
should the cellsInUse.GetLength(0) be cell.GetLength(0) => j and cell.GetLength(1) => k
I have 2 datagridviews. dataGridView1 has 3 rows and dataGridView2 has 2 rows.How to compare the values between them.
Exa: Datagridview1 Datagridview2
Id name Id name
1 A 3 C
2 B 4 A
3 C
I want to compare:
1 vs 3, 1 vs 4,
2 vs 3, 2 vs 4,
3 vs 3, 3 vs 4.
I use the code below but it works incorrect.
for (int i = 0; i < dataGridView1.RowCount; i++)
{
for (int j = 0; j < dataGridView2.RowCount; j++)
{
i++;
string grid2 = dataGridView2.Rows[j].Cells[1].Value.ToString();
string grid1 = dataGridView1.Rows[i].Cells[1].Value.ToString();
if (grid1 == grid2 || dataGridView2.Rows[0].Cells[1].Value.ToString() == dataGridView1.Rows[0].Cells[1].Value.ToString() )
{
dataGridView1.Rows.RemoveAt(i);
}
}
}
1)
If you want to iterate through a collection using a for loop it is not wise to increment the indexing variable additionally in the code with this line:
i++;
it is already incremented. So when you run through the dataGridView2 you actually slide one position further also in dataGridView1. This is not what you want (as I see from your post where you describe the desired comparisons). You need to remove this line
2)
I want to compare: 1 vs 3, 1 vs 4, 2 vs 3, 2 vs 4, 3 vs 3, 3 vs 4.
if you want to compare only the name column then your indexing is right :
dataGridView2.Rows[j].Cells[1]
but if you want to compare the Id column then it is wrong because indexing starts with 0. So to get the correct value from the Id column you have to use it like this:
string grid2 = dataGridView2.Rows[j].Cells[0].Value.ToString();
With this comparison in your if-condition:
dataGridView2.Rows[0].Cells[1].Value.ToString() == dataGridView1.Rows[0].Cells[1].Value.ToString()
you additionally compare A with C. I don't see any reason for this (taken your description about the comparison desire). You can get rid of it.
3)
If you want to change the size of a collection (by deleting elements) through which you iterate it is wise to use a reversed for-loop which starts from the dataGridView2.RowCount-1 and runs until >= 0. Otherwise you risk to run into an ArgumentOutOfBounds exception because you don't have as much elements after removal as you did when you started the loop. The element at the end index will not exist anymore.
for (int i = dataGridView1.RowCount-1; i >= 0 ; i--)
{
for (int j = 0; j < dataGridView2.RowCount; j++)
{
string grid2 = dataGridView2.Rows[j].Cells[0].Value.ToString();
string grid1 = dataGridView1.Rows[i].Cells[0].Value.ToString();
if (grid1 == grid2)
{
dataGridView1.Rows.RemoveAt(i);
}
}
}
EDIT:
The dataGridView1 might display an additional empty row which is also counted in dataGridView1.RowCount in this case you need to start from i = dataGridView1.RowCount-2. Also after you have found a match and removed the row from dataGridView1 it does not make sense to search further for matches for this row, since it is gone. So you should break out of the inner loop and proceed with the next line:
for (int i = dataGridView1.RowCount - 2; i >= 0; i--)
{
for (int j = 0; j < dataGridView2.RowCount -1; j++)
{
string grid2 = dataGridView2.Rows[j].Cells[0].Value.ToString();
string grid1 = dataGridView1.Rows[i].Cells[0].Value.ToString();
if (grid1 == grid2)
{
dataGridView1.Rows.RemoveAt(i);
break;
}
}
}
Working on a "Find the Island" game program. Will read in the number of rows and columns from the user and create a game map for play, using a char[][] array. Problem is, while it compiles and assigns the first character with no problem, after that I get an IndexOutOfRange exception and have to terminate the program. The code snippet is posted below, with the offending line marked. How do I fix this?
char[][] Waves = new char[Rows + 1][];
for (int counter = 0; counter < Rows+1; counter++)
Waves[counter] = new char[Columns];
MapRows = Rows;
MapCols = Columns;
for (int c = 0; c < Rows; c++)
{
for (int d = 0; d < Columns; d++)
{
if (c == 0 && d == 0)
Waves[c][d] = 'X';
else if (c == 0)
Waves[c][d] = (char)d; // Offending line here
else if (d == 0)
Waves[c][d] = (char)c;
else
Waves[c][d] = '~';
}
}
The map should appear as follows for a 3x3 map (spaces and line breaks will be added when printing):
X123
1~~~
2~~~
3~~~
Second, and much less important, I plan on printing the map to a read-only text box. How do I write it to the text box where I print a row of characters from the array, before ending that line and printing the next row of characters? The for loops are easy, getting it to print is not.
I have this for loop. TicketList starts with 109 tickets. nColumns = 100. I calculate the number of rows I will need depending on the number of tickets. So in this case I need 2 rows. Row one will be full and row two will only have 9 entries. I have the loop below. It only runs one time for the NumOfRows and fills the first 100 and never loops.
What am I missing?
for (int j = 0; j < NumOfRows; j++)
{
for (int i = 0; i < nColumns; i++)
{
if (TicketList.Count() > 0)
{
t = rand.Next(0, TicketList.Count() - 1);
numbers[i, j] = TicketList[t];
TicketList.Remove(TicketList[t]);
}
}
}
Try changing your code to use a more LINQ-like, functional approach. If might make the logic easier. Something like this:
TicketList
.OrderBy(x => rand.Next())
.Select((ticket, n) => new
{
ticket,
j = n / NumOfRows,
i = n % NumOfRows
})
.ToList()
.ForEach(x =>
{
numbers[x.i, x.j] = x.ticket;
});
You may need to flip around x.i & x.j or use nColumns instead of NumOfRows - I wasn't sure what your logic was looking for - but this code might work better.
Other than a few poor choices, your loops appear to be fine. I would venture that NumOfRows is not being calculated correctly.
The expression NumOfRows = (TotalTickets + (Columns - 1)) / Columns; should calculate the correct number of rows.
Also, you should use the property version of Count rather than the Linq extension method and use IList<T>.RemoveAt() or List<T>.RemoveAt rather than Remove(TicketList[T]).
Using Remove() requires that the list be enumerated to locate the element to remove, which may not be the same index that you are targeting. Not to mention that you will scan 50% (on average) of the list for each Remove call, when you already know the correct index to remove.
The functional approach listed earlier seems like overkill.
I've attempted to replicate your issue, assuming certain facts about the various variables in use. The loop repeats the expected number of times.
static void TestMe ()
{
List<object> TicketList = new List<object>();
for (int index = 0; index < 109; index++)
TicketList.Add(new object());
var rand = new Random();
int nColumns = 100;
int NumOfRows = (TicketList.Count + (nColumns - 1)) / nColumns;
object[,] numbers;
int t;
numbers = new object[nColumns, NumOfRows];
for (int j = 0; j < NumOfRows; j++)
{
Console.WriteLine("OuterLoop");
for (int i = 0; i < nColumns; i++)
{
if (TicketList.Count > 0)
{
t = rand.Next(0, TicketList.Count - 1);
numbers[i, j] = TicketList[t];
TicketList.RemoveAt(t);
}
}
}
}
The problem that you are seeing must be the result of something that you have not included in your sample.
This question already has answers here:
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 7 years ago.
Unhandled Exception: System.IndexOutOfRangeException: Index was outside of the bounds of the array (at the first if statements)
static int arrayRows = 20;
static int arrayCols = 20;
public void printBoard()
{
int neighbours;
for (y = 0; y < arrayCols; y++)
for (x = 0; x < arrayRows; x++)
{
neighbours = 0;
// Count number of neighbours surrounding live cell
if (parentGen[x-1, y-1] == 1) // top left
neighbours++;
if (parentGen[x-1, y] == 1) // left
neighbours++;
if (parentGen[x-1, y+1] == 1) // bottom left
neighbours++;
if (parentGen[x, y-1] == 1) // middle top
neighbours++;
if (parentGen[x, y+1] == 1) // middle bottom
neighbours++;
if (parentGen[x+1, y-1] == 1) // top right
neighbours++;
if (parentGen[x+1, y] == 1) // right
neighbours++;
if (parentGen[x+1, y+1] == 1) // bottom right
neighbours++;
}
}
The only thing I can think of is that my program is checking coordinates of < 0? How do I go about fixing this?
Your first coordinates are parentGen[-1, -1], this will always throw the exception.
You need to check if the cell you're on has any neighbors to the left, right, top, or bottom. For example, x = 0 has no neighbors to the left and y = 20 has no neighbors to the bottom. You may wish to break this out to other functions, such as HasNeighborsLeft(int x), etc.
edit: sample code
if(x > 0 && y > 0 && parentGen[x - 1, y - 1] == 1)
{
neighbors++;
}
You can factor this out to it's own functions though, and you can wrap this kind of logic around all of the checks that involve x - 1 for example.
You need boundary condition checks on both x and y at top and bottom of their range. You cannot legally index the entire array using +1 and -1 offsets. Break up your check into boundary condition cases where x == 0, x == arrayRows-1 (by not checking the invalid relative offsets here), and then check cases where x+1 and x-1 are always valid in an else. Similarly with y.
You're array goes from 0->21. As well, you're testing values at [-1, -1] and [22, 22]You can fix it by chainging your for statement(s) to
for (int x = 1; x <= arrayCols - 1; x++)
for (int y = 1; y <= arrayRows - 1; y++)
In addition, problems with loops are almost always caused by a small number of cases that you can always check for:
Your for statement a) starts at a lower bound than the array, or b) finishes at a higher one
a) for (int x = -1;
b) for (int x = 0; x <= array.Length
Your code in the loop accesses values outside of your indexer range
for (int x = 0...
array[x-1, ...
Your collection isn't initialized
In this case, your problem 2.
The problem is that you are looking at the previous and next values (-1 and +1) which will obviously go outside the array bounds at either ends of the array.
There are a few options solving this:
Create a bigger array with a dummy 'border' around the edge which you don't use for your board but allows you to use code very similar to that which you have now (with your -1 and +1 previous and next cell logic). (Imagine a chess board that is 10x10 where you are not allowed to play in the outermost squares).
Scatter loads of 'if' statements to check if you're at the first or last item in the array and thus avoid making any array accesses that are invalid.
Create a function to retrieve an item at a particular cell and put conditional logic in this function for dealing with the array bounds.
Personally I would go with the last option, build yourself a function which gets the state of the specified cell, checks that the indices are valid and returns a default value if they are not. For example:
private const int EMPTY_CELL = 0;
private const int INVALID_CELL = EMPTY_CELL; // for now the same, but gives scope to handle separately
private int GetCellState(int row, int column)
{
if (row < 0) return INVALID_CELL;
if (column < 0) return INVALID_CELL;
if (row >= arrayRows) return INVALID_CELL;
if (column >= arrayColumns) return INVALID_CELL;
return parentGen[row, column];
}
It is then simply a matter of swapping your direct accesses to parentGen with calls to the function.
you could start by creating a sequence of only the valid indices and then iterate the combinations of those:
static int arrayRows = 20;
static int arrayCols = 20;
public void printBoard()
{
var sequences = from row in Enumerable.Range(0, arrayRows)
from column in Enumerable.Range(0, arrayCols)
select new
{
Rows = (from xs in new[] { row - 1, row, row + 1 }
where xs >= 0 && xs < 20
select xs),
Columns = (from ys in new[] { column - 1, column, column + 1 }
where ys >= 0 && ys < 20
select ys)
};
//now that we have a sequence with all the needed (valid) indices
//iterate through the combinations of those
var neighbours = (from seq in sequences
from row in seq.Rows
from column in seq.Columns
where row != column && parentGen[row, column] == 1
select 1).Count();
}