Right method to check if DateTime is greater than another DateTime - c#

I want to check if a DateTime in CustomFormat is greater than another DateTime, but the checking is okay after that the code removes a lot of item and none of that correct. Here is my code. The job is to remove any ListViewItem which is older than 3 days.
for (int i = 0; i < lvValid.Items.Count; i++)
{
if (DateTime.Now.AddDays(-3) > DateTime.Parse(lvValid.Items[i].SubItems[1].Text))
{
lvValid.Items[i].Remove();
}
}
I've already tried foreach but it didn't help. And its removing every second element, but its not correct

One big problem with a forward loop is that if you remove one item, then the indices of the remaining will shift leftwards. So at i == 1 the next item will be at 1 after the removal. Then i will be incremented and skip the former next item.
I would suggest to use a backward loop:
for (int i = lvValid.Items.Count - 1; i >= 0 ; i--)
{
if (DateTime.Now.AddDays(-3) > DateTime.Parse(lvValid.Items[i].SubItems[1].Text))
{
lvValid.Items[i].Remove();
}
}
personally I would advice to work with the original data. First filter the data according to your condition and then repopulate the ListView.

Maybe you should Reverse for loop & not use .AddDays(-3)
for (int i = lvValid.Items.Count - 1; i >= 0; i--)
{
if (DateTime.Now <= DateTime.Parse(lvValid.Items[i].SubItems[1].Text).AddDays(3);
{
lvValid.Items[i].Remove();
}
}

If you want to use forward loop (if order of removing matters) you should increment i++ when you don't remove an item:
// we don't want redrawing on each removing (i.e. lvValid blicking)
lvValid.BeginUpdate();
try
{
for (int i = 0; i < lvValid.Items.Count; ) // don't icrement i here...
if (DateTime.Now.AddDays(-3) > DateTime.Parse(lvValid.Items[i].SubItems[1].Text))
lvValid.Items[i].Remove();
else
++i; // ... but there
}
finally
{
// when finisihing removing, redraw lvValid if required
lvValid.EndUpdate();
}

Related

How to make this code more functional or 'prettier'

I've been working on a project where I need on a button press that this line gets executed.
if (listView1.SelectedItems[0].SubItems[3].Text == "0") //Checks to see Value
{
listView1.SelectedItems[0].SubItems[3].Text = "1";// If Value is Greater, Increase and Change ListView
questionNumberLabel.Text = listView1.SelectedItems[0].SubItems[3].Text;// Increase and Change Label
}
Now I have this repeated about 10 times with each value increasing by one. But I know that this is ugly, and dysfunctional. As well as conflates the file size. I've tried a few things. Primarily this method.
if (listView1.SelectedItems[0].SubItems[3].Text == "0")
{
for (var i = 1; i < 100;)
{
if (!Int32.TryParse(listView1.SelectedItems[0].SubItems[3].Text, out i))
{
i = 0;
}
i++;
listView1.SelectedItems[0].SubItems[3].Text = i.ToString();
Console.WriteLine(i);
}
}
But instead of just adding one, it does the 100 instances and ends. The reason this is becoming a pain in the *** is because the
listView1.SelectedItems[0].SubItems[3].Text
is just that - it's a string, not an int. That's why I parsed it and tried to run it like that. But it still isn't having the out come I want.
I've also tried this
string listViewItemToChange = listView1.SelectedItems[0].SubItems[3].Text;
Then parsing the string, to make it prettier. It worked like it did before, but still hasn't given me the outcome I want. Which to reiterate is, I'm wanting the String taken from the list view to be changed into an int, used in the for loop, add 1, then restring it and output it on my listView.
Please help :(
You say you want the text from a listview subitem converted to an int which is then used in a loop
so - first your creating your loop variable, i, then in your loop you're assigning to it potentially 3 different values 2 of which are negated by the, i++. None of it makes sense and you shouldn't be manipulating your loop variable like that (unless understand what you're doing).
if you move statements around a little..
int itemsToCheck = 10; // "Now I have this repeated about 10 times "
for (var item = 0; item < itemsToCheck; item++)
{
int i;
if (!Int32.TryParse(listView1.SelectedItems[item].SubItems[3].Text, out i))
{
i = 0;
}
i++;
listView1.SelectedItems[item].SubItems[3].Text = i.ToString();
Console.WriteLine(i);
}
Something along those lines is what you're looking for. I haven't changed what your code does with i, just added a loop count itemsToCheck and used a different loop variable so your loop variable and parsed value are not one in the same which will likely be buggy.
Maybe this give you an idea. You can start using this syntax from C# 7.0
var s = listView1.SelectedItems[0].SubItems[3].Text;
var isNumeric = int.TryParse(s, out int n);
if(isNumeric is true && n > 0){
questionNumberLabel.Text = s;
}
to shortcut more
var s = listView1.SelectedItems[0].SubItems[3].Text;
if(int.TryParse(s, out int n) && n > 0){
questionNumberLabel.Text = s;
}

How to exclude a statement from first iteration of a loop in c#?

I am a beginner in C#. i want to implement this concept.
for (int i = 0; i < TestCases.Count; i++)
{
// some statements
if (i > 0)
{
// some statements
Runthisfunction();
// some statements
}
}
I want to run them in the way showns above.
Inshort, i want Runthisfunction() to execute from second iteration. i don't know how to do that. can you please guide me ?
what you have will work; there are other things you could do, though - for example:
if(TestCases.Count != 0) { abcdef on item 0 }
for(int i = 1 ; i < TestCases.Count ; i++)
{
abcdef;
Runthisfunction();
efg
}
This just changes the loop to start at 1 instead of 0, and runs 0 manually.
You can create and change the flag to do this example is following
bool flag=false;
for(int z=0;z<TestCases.Count;z++)
{
if(z!=0)
flag=true;
if(flag)
RunthisFunction();
}
you can achieve your goal with this technique! because Runthisfunction(); will not execute at first iteration!

for loop ends prematurely when objects are removed

Hi I have a problem with a for loop.
It looks like this
for (int i = 0; i < ObjectManager.Instance.Objects.Count; i++)
{
if (ObjectManager.Instance.Objects[i] is Asteroid)
{
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
}
}
But the count gets shorter while I remove objects, which causes the loop to end prematurely. Is there a way to do this without a bunch of extra loops.
Why don't you loop backward?
// Just change the order from Count - 1 down to 0
for (int i = ObjectManager.Instance.Objects.Count - 1; i >= 0; --i)
{
if (ObjectManager.Instance.Objects[i] is Asteroid)
{
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
}
}
In case you have to loop forward (e.g. if Instances should be deleted in the order they are created because they are depend on each other) you can modify for loop in this way:
for (int i = 0; i < ObjectManager.Instance.Objects.Count;) // <- No increment here
if (ObjectManager.Instance.Objects[i] is Asteroid)
ObjectManager.Instance.Objects.Remove(ObjectManager.Instance.Objects[i]);
else
i += 1; // <- Increment should be here!
Yet another possibility is Linq:
ObjectManager.Instance.Objects.RemoveAll(item => item is Asteroid);
Three options:
If ObjectManager.Instance.Objects is a List<T>, use List<T>.RemoveAll with a predicate, making your code much simpler:
// This replaces your whole loop...
ObjectManager.Instance.Objects.RemoveAll(x => x is Asteroid);
Count from the end of the collection rather than from the start, so that you don't need to adjust the index afterwards:
for (int i = ObjectManager.Instance.Objects.Count - 1; i >= 0; i--)
Just decrement i after calling Remove, so that you'll look at the right index on the next iteration.
Note that in the second and third options your code will be a lot simpler to read if you extract the expression ObjectManager.Instance.Objects into a local variable before you use it 4 times. Also consider using RemoveAt(i) rather than Remove(instances[i]), assuming RemoveAt is available for the type you're using.

How to loop through checkedlistbox and remove selected items in c#

In my app users can add some item in checkedlistbox, then user selects some element and clicks the button "Remove". How can I loop through my checkedListBox and remove selected items?
You can check the count of checked items and remove on while loop as below
while (checkedListBox1.CheckedItems.Count > 0) {
checkedListBox1.Items.Remove(checkedListBox1.CheckedItems[0]);
}
OR
int lastIndex =checkedListBox1.Items.Count-1;
for(int i=lastIndex ; i>=0 ; i--)
{
if (checkedListBox1.GetItemCheckState(i) == CheckState.Checked)
{
checkedListBox1.Items.RemoveAt(i);
}
}
Try it. It is working code
for (int i = 0; i < CheckBoxList1.Items.Count; i++)
{
if (CheckBoxList1.Items[i].Selected)
{
CheckBoxList1.Items.RemoveAt(i);
i--;
}
}
The trick to removing certain elements from a list of those elements is to walk the list in reverse - from count-1 to 0. That way when you remove an element, the indexes of remaining ones you still care about have not changed.

Row Index provided is out of range, even after check

My current code:
Remove()
{
for (int i = 0; i < ConGridView.RowCount; i++)
{
if (ConGridView.Rows[i].Cells[0].Value.ToString() == Address)
{
ConGridView.Rows.RemoveAt(i);
break;
}
}
}
So what I am trying to call the remove function every time a client disconnect. the function will remove the connection address from the datagridview. It works well when clients are disconnection one by one. However, if 100 connections gets dropped and it tries to remove 100 connections in less than a second, than it errors out saying "Row Index provided is out of range". How should I check for that ?
So far I've tried:
Try, catch.
if (ConGridView.Rows[i] != null), if (i < ConGridView.RowCount)
None of it seem to work so far. I've also got results using (i < ConGridView.RowCount) where i is 26 while RowCount is 24, but the remove at function still activates..
Any idea on this ?
You can't do this. Your code loops through all the rows in ConGridView, but it deletes them as you do. Therefore, at some point you will try to access an item you have deleted, which will cause the error you described.
Probably the best approach it to iterate through the rows in reverse order. This way, deleting a row at the end won't affect when you access rows at the start.
The problem is you initialise your for loop with the current count of rows and then start removing those same rows from the datagridview. At some point your for loop will try to remove a row at an index that is greater than the number of rows left.
Try this instead:
for (int i = ConGridView.RowCount - 1; i >= 0; i--)
{
if (ConGridView.Rows[i].Cells[0].Value.ToString() == Address)
{
ConGridView.Rows.RemoveAt(i);
break;
}
}
why dont you get the total count to a separate variable and then iterate
Remove()
{
int totalConnections = ConGridView.RowCount;
for (int i = 0; i < totalConnections ; i++)
{
if (ConGridView.Rows[i].Cells[0].Value.ToString() == Address)
{
ConGridView.Rows.RemoveAt(i);
break;
}
}
}
This issue is becuase you are modifying the collection your are iterating over. It will be better if you use a temporary array and two loops to remove your entries.
Remove()
// You can use an array/list or whatever you want below.
Collection<DataGridViewRow> rowsToDelete = new Collection<DataGridViewRow>();
for (int i = 0; i < ConGridView.RowCount; i++)
{
if (ConGridView.Rows[i].Cells[0].Value.ToString() == Address)
{
rowsToDelete.Add(ConGridView.Rows[i]);
break;
}
}
// now remove the marked entries.
foreach(DataGridViewRow deletedRow in rowsToDelete)
{
ConGridView.Rows.Remove(deletedRow);
}
When you remove an item from an array, it is reconstructed; shifting the remaining elements up by one to remove the gap of the index you have removed.
1. guybrush threepwood
2. murray
3. elaine
4. Jimmy Gibbs Jr.
If you remove 2. item in here; it becomes this:
1. guybrush threepwood
2. elaine
3. Jimmy Gibbs Jr.
When you are iterating, imagine:
for (int i = 0; i < myArray.Count; i++)
{
if (i == 2) myArray.RemoveAt(i);
}
While running this, when i = 3, the element at 3 has changed, you expect it to be 'elaine' but it is 'Jimmy Gibbs Jr.'. One way to fix this is decrease i by one if we delete it, making sure that i refers to correct value.
for (int i = 0; i < myArray.Count; i++)
{
if (i == 2)
{
myArray.RemoveAt(i);
i--;
}
}
I would go for LINQ in this case, though, everything is easier with that.
myArray.RemoveAll(x => x == "murray");
I've tried all the suggestions posted by everyone here, however, the error was still there.
I've solved the problem using a different way... I've switched to TreeNodeView since that's what I was going to use ultimately. Now I can remove as many connection as i want with:
For each(TreeNode TN in ConTreeView)
{
ConTreeView.Nodes.Remove(TN);
}

Categories

Resources