Compare value between 2 datagridview c# - c#

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;
}
}
}

Related

How to solve an index out of bounds issue, when you have to take into consideration both neighbors of an array's element?

This thing that I'm writing should do the following: get as an input a number, then, that many kids' names, and that many grades. Then, assign each kid a number of coins, so that if their grade is bigger, than their neighbor's, they get more coins and vice versa. What I wrote is this:
string input = Console.ReadLine();
int n = Convert.ToInt32(input);
int i = 0;
string[] names = new string[n];
for (i = 0; i < n; i++)
{
names[i] = Console.ReadLine();
}
string[] gradeText = new string[n];
int[] grades = new int[n];
for (i = 0; i < n; i++)
{
gradeText[i] = Console.ReadLine();
grades[i] = Convert.ToInt32(gradeText[i]);
}
int[] minCoins = { 1, 2, 3 };
int[] coinArray = new int[n];
for (i = 1; i < n - 2; i++)
{
if (grades[0] > grades[1])
{
coinArray[0] = 3;
}
else
{
coinArray[0] = 1;
}
if (grades[i] > grades[i + 1] && grades[i] > grades[i - 1])
{
coinArray[i] = 3;
}
if (grades[i] > grades[i + 1] || grades[i] > grades[i - 1])
{
coinArray[i] = 2;
}
if (grades[i] < grades[i + 1] && grades[i] < grades[i - 1])
{
coinArray[i] = 1;
}
if (grades[n - 1] > grades[n - 2])
{
coinArray[n - 1] = 3;
}
else
{ coinArray[n - 1] = 1; }
}
for (i = 0; i < n; i++)
{
Console.WriteLine(names[i] + " " + coinArray[i]);
}
I know my loop is hella messed up, but any tips on how to fix it would be kindly appreciated!
Others here have already suggested how to deal with index out of bounds issues. So this is slight different approach to solving your problem.
It is very easy to see this as one problem and then try to resolve it all in one place but that isn't always the best solution.
Your for loop is trying to do quite a lot. Each iteration could have many checks to make. In addition you are making checks you have previously made.
Do I have a neighbour to the left.
Do I have a neighbour to the right.
Did I get a better grade than both neighbours.
Did I get a better grade than one neighbour.
Did I lose to both neighbours.
My advice would be to break this down into two separate tasks.
1, To calculate how many neighbours each person got a higher grade than.
string[] names = new string[]{"John", "Paul", "Ringo", "George"};
int[] grades = new[] {3, 4, 3,2};
int[] winnersandloser = new int[4];
for (int i = 1; i < grades.Length; i++) //note starting at position 1 so I dont need to handle index out of bounds inside the for loop
{
if (grades[i] > grades[i - 1])
{
winnersandloser[i]++;
}
else
{
winnersandloser[i - 1]++;
}
}
In the above code you should have an array with these values: {0,2,1,0}
0 = you lost to both neighbours
1 = you beat one neighbour
2 = well done you beat both neighbours
Then using this winnersandlosers array you can calculate how many coins to give each person. I'll leave that for you to do.
Update
If you require different behaviour for the first and last in the list of people you need to add the logic to your code for allocating coins.
An array gives each value and index value, starting from 0. So 0 points to the first value in you array. George is the 4th entry in the array but as the array index starts with 0 the index value is 3, you also get this from arrayname.Length - 1
So now when looping through the array to allocate coins we can add a check for the first and last positions in the array.
//allocating coins
for (int i = 0; i < winnersandloser.Length; i++)
{
if (i == 0 || i == winnersandloser.Length - 1)
{
//allocating rules for first and last
}
else
{
//allocating rules for everyone else
}
}
One common way to approach this kind of issue is to oversize your array by as many elements as you need to look ahead/look behind. Place you real elements in the "middle"1 of this array and suitable dummy values into the elements at the start/end that don't correspond to real entries. You pick the dummy values such that the comparisons work out how you need them to (e.g. often you'll put int.MinValue in the dummy elements at the start and int.MaxValue in the dummy elements at the end).
You then just enumerate the real elements in the array, but all of your computed look aheads/look behinds still correspond to valid indexes in the array.
1Sometimes you'll have uneven look ahead/look behind requirements so it may not be the true middle. E.g. say you need to be able to look behind one element and ahead 3 elements, and you want to process 20 elements. You then create an array containing 24 entries, put dummy values at index 0, 21, 22 and 23, and populate your real elements into indexes 1 - 20.

How to delete multiple rows from a datagridview by selecting the checkboxes in C#?

I have datagridview and header checkbox to check all the check boxes in the rows in the checkbox column (index of the column for all the check boxes 0).
I have following code in the deleted button event:
for (int i = 0; i < datagridview.Rows.Count; i++) {
if (Convert.ToBoolean(datagridview.Rows[i].Cells[0].Value) == true) {
datagridview.Rows.RemoveAt(i);
}
}
The datagridview.rows.count keeps decreasing as the row deleted but I in the loop keeps increasing. Thus, instead of deleting all the checked rows, it deletes some of them only. It does not delete the one with rows index 0, 1, 2 and so on with the new data in the datagridview as I increase in the code.
The problem is that by keep increasing the index on deletion, you skip the next item in the list:
0: [X] Item 0
1: [X] Item 1
2: [X] Item 2
on iteration 0 ... we delete Item 0 on index 0 OK
0: [X] Item 1
1: [X] Item 2
on iteration 1 ... we delete Item 2 on index 1 BUT we skipped Item 1 which is now on index 0
Therefore, you should iterate the list in the reverse order, that way when you remove an item, the next previous item's index is still the same:
for (int i = datagridview.Rows.Count; i >= 0 ; i--)
{
// ....
}
I solved the issue after getting hints from Charles as below. Thank you!
for (int i = 0; i < dgvBaleDisposition.Rows.Count; i++)
{
if (Convert.ToBoolean(dgvBaleDisposition.Rows[i]
.Cells[0].Value) == true)
{
dgvBaleDisposition.Rows.RemoveAt(i);
}
i = i - 1;
j = j + 1;
if (j == countRows)
{
break;
}
}

How to copy a row range from one column to others in a column oriented c# datatable?

To my understanding the entries of a datatable are usually row oriented where the columns have a certain meaning and the rows indicate different datasets.
This is also why the actual data is added with DataTable.Rows.Add()
In my case the columns are considered one dataset and I need to extract data from these columns for use in other areas.
I am using a combination of LINQ and Lambda expressions to get the data of one complete column:
int curCol = 4;
int maxRows = 55;
byte[] values = new byte[maxRows];
values = dt.Rows.Cast<DataRow>().Select<DataRow, byte>(row => Convert.ToByte(row[curCol])).ToArray();
But this is where my luck with LINQ and Lambda ends.
I try to implement a copy routine to select columns different from the source but having the same value in a certain row. Then I select a range of rows to be copied to all the other matching columns. Here is an example how this works with for and if statements:
const int numCols = 10;
int curCol = 2;
int searchRow = 1;
int startRow = 3;
int numRows = 25;
byte val = (byte)dt.Rows[searchRow][curCol];
// iterate through all columns
for (int col = 0; col < numCols; col++)
{
// look for "other" columns with the same value in the searchRow of interest
if (col != curCol && val == (byte)dt.Rows[searchRow][col])
{
// iterate through the given row range (startRow and numRows)
for (int row = startRow; row < startRow+numRows; row++)
{
// copy from current column
dt.Rows[row][col] = dt.Rows[row][curCol];
}
}
}
I was wondering if there is a nicer, more efficient way to implement this Using LINQ and Lambda expressions?
Example Data
1 2 3 4 ... // cols 0 .. 3 in row 0
5 5 6 6 ... // cols 0 .. 3 in row 1
0 0 1 0 ... // ...
7 0 8 0 ...
9 0 9 0 ...
. . . .
Expected Result
1 2 3 4 ...
5 5 6 6 ... // value in col 3 is equal to value in col 2
0 0 1 0 ...
7 0 8 8 ... // value from col 2 copied to col 3
9 0 9 9 ... // value from col 2 copied to col 3
. . . .
I hope this makes it easier to understand. Column 2 and 3 are grouped/linked by their value in row 1 and since column 2 is the source other values from a selected row range should be copied over to the linked colums.
Just to make that clear. The If/For implementation above does exactly this. I was just hoping for a LINQ/Lambda shortcut or another more efficient way of execution.
I don't see much:
DataTable dt = new DataTable();
const int numCols = 10;
int curCol = 4;
int searchRow = 1;
int startRow = 3;
int numRows = 25;
int hashVal = dt.Rows[searchRow][curCol].GetHashCode();
var thisValue = dt.Rows[searchRow][curCol];
//iterate cols and rows
for(int c = 0; c < numCols; c++)
{
for(int r = startRow; r < startRow + numRows; r++)
{
int thisHash = dt.Rows[r][c].GetHashCode();
if (thisHash == hashVal)
{
dt.Rows[r][c] = thisValue;
}
}
}
Nothing ground-shaking. What I don't understand, though, is that you seem to be finding matching values in order to copy matching values - by default, if the value is already equal to the source row/col, then why does it need to be copied (because it's already equal)? Perhaps you're simplifying for demonstration purposes, but I'm not seeing a need for this...

C# Printing a border around a 2D Array

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

C# looping through an array

I am looping through an array of strings, such as (1/12/1992 apple truck 12/10/10 orange bicycle). The array's length will always be divisible by 3. I need to loop through the array and grab the first 3 items (I'm going to insert them into a DB) and then grab the next 3 and so on and so forth until all of them have been gone through.
//iterate the array
for (int i = 0; i < theData.Length; i++)
{
//grab 3 items at a time and do db insert, continue until all items are gone. 'theData' will always be divisible by 3.
}
Just increment i by 3 in each step:
Debug.Assert((theData.Length % 3) == 0); // 'theData' will always be divisible by 3
for (int i = 0; i < theData.Length; i += 3)
{
//grab 3 items at a time and do db insert,
// continue until all items are gone..
string item1 = theData[i+0];
string item2 = theData[i+1];
string item3 = theData[i+2];
// use the items
}
To answer some comments, it is a given that theData.Length is a multiple of 3 so there is no need to check for theData.Length-2 as an upperbound. That would only mask errors in the preconditions.
i++ is the standard use of a loop, but not the only way. Try incrementing by 3 each time:
for (int i = 0; i < theData.Length - 2; i+=3)
{
// use theData[i], theData[i+1], theData[i+2]
}
Not too difficult. Just increment the counter of the for loop by 3 each iteration and then offset the indexer to get the batch of 3 at a time:
for(int i=0; i < theData.Length; i+=3)
{
var item1 = theData[i];
var item2 = theData[i+1];
var item3 = theData[i+2];
}
If the length of the array wasn't garuanteed to be a multiple of three, you would need to check the upper bound with theData.Length - 2 instead.
Your for loop doesn't need to just add one. You can loop by three.
for(int i = 0; i < theData.Length; i+=3)
{
string value1 = theData[i];
string value2 = theData[i+1];
string value3 = theData[i+2];
}
Basically, you are just using indexes to grab the values in your array. One point to note here, I am not checking to see if you go past the end of your array. Make sure you are doing bounds checking!
This should work:
//iterate the array
for (int i = 0; i < theData.Length; i+=3)
{
//grab 3 items at a time and do db insert, continue until all items are gone. 'theData' will always be divisible by 3.
var a = theData[i];
var b = theData[i + 1];
var c = theData[i + 2];
}
I've been downvoted for this answer once. I'm pretty sure it is related to the use of theData.Length for the upperbound. The code as is works fine because array is guaranteed to be a multiple of three as the question states. If this guarantee wasn't in place, you would need to check the upper bound with theData.Length - 2 instead.
Here is a more general solution:
int increment = 3;
for(int i = 0; i < theData.Length; i += increment)
{
for(int j = 0; j < increment; j++)
{
if(i+j < theData.Length) {
//theData[i + j] for the current index
}
}
}
string[] friends = new string[4];
friends[0]= "ali";
friends[1]= "Mike";
friends[2]= "jan";
friends[3]= "hamid";
for (int i = 0; i < friends.Length; i++)
{
Console.WriteLine(friends[i]);
}Console.ReadLine();

Categories

Resources