This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Foreach loop, determine which is the last iteration of the loop
foreach (DataRowView row in orderedTable.DefaultView)
{
if(lasttime) do-something;
}
orderedtable is a datatable
does anyone know how to find out whether we are on the last foreach iteration? please keep in mind that i do have duplicates in orderedtable
The correct method that works in all cases is to use the IEnumerator<T> directly:
using (var enumerator = orderedTable.DefaultView.GetEnumerator())
{
if (enumerator.MoveNext())
{
bool isLast;
do
{
var current = enumerator.Current;
isLast = !enumerator.MoveNext();
//Do stuff here
} while (!isLast);
}
}
This method works even if your collection doesn't have a Count property, and even if it does, this method will be more efficient if the Count property is slow.
The foreach construct does not know such a thing, since it applies equally to unbounded lists. It just has no way of knowing what is a last item.
You can iterate the manual way as well, though:
for (int i = 0; i < orderedTable.DefaultView.Count; i++) {
DataRowView row = orderedTable.DefaultView[i];
if (i == orderedTable.DefaulView.Count - 1) {
// dosomething
}
}
An alternative approach which I don't think anyone posted. This works well if you don't know the count ahead of time.
DataRowView lastRow;
foreach (DataRowView row in orderedTable.DefaultView)
{
// Do something...
lastRow = row;
}
if (lastRow != null)
{
// Do something with last row
}
You will have to use a regular for loop if you want to have different behavior on the last item.
for (int i = 0; i < orderedTable.DefaultView.Count; i++)
{
//do stuff
if (i == orderedTable.DefaultView.Count - 1)
{
//do additional special stuff
}
}
It's worth noting that "the other Skeet" has an implementation for a "smart enumerable" which supports a Last property. See the article here: http://msmvps.com/blogs/jon_skeet/archive/2007/07/27/smart-enumerations.aspx
With this you could write something like this (I might get the details wrong, haven't tried it out myself):
foreach (SmartEnumerable<DataRowView> item in new SmartEnumerable<DataRowView>(orderedTable.DefaultView))
{
DataRowView row = item.Value;
if(item.IsLast)
{
///do special stuff
}
}
Instead of using foreach get the IEnumerator. If MoveNext returns null the previous was the last one.
for would work too of course.
You could possibly do something like the following:
foreach (DataRowView row in orderedTable.DefaultView)
{
if(row == orderedTable.DefaultView.Last()) do-something;
}
But its pretty inefficient.
If you're concerned in keeping track of your iteration, why not use a for instead or a foreach? Then you can simply do the following:
for(int i = 0; i<orderedTable.DefaultView.Count-1;i++)
{
if(i==orderedTable.DefaultView.Count-1)
{
// Last Row
}
}
The code you have posted is identical to:
if (orderedTable.DefaultView.Rows.Count > 0)
do-something;
If that does not do what you want, you will have to explain what lastTime does.
If I understand your question, does this help?
int lastrow = 0;
foreach (DataRow row in table.Rows) // Loop over the rows.
{
lastrow++;
// Do Something
if (lastrow == (table.Rows.Count - 1))
{
// Do something else
}
}
Related
So I am trying to loop though items that are in a listbox in my application. The list box will allow you to select multiple items to which I have a method tied to each item in the listbox. I have a counter variable incremented each time the loop works.When I use the foreach loop with the switch statement below, it does the first item correct, but then loops through the same item again. I know I am missing something as it is supposed to go to the next item in the listbox and not the same item.
string reportname = lstbxReports.SelectedValue.ToString();
int i = 0;
foreach (var report in reportname)
{
switch (reportname)
{
case "Overview":
{
if (i < 1)
{
PrintOverview(filename);
}
else if (i >= 1)
{
PrintOverviewAppend(filename);
}
break;
}
case "Sources":
{
if (i < 1)
{
PrintSource(filename);
}
else if (i >= 1)
{
PrintSourceAppend(filename);
}
break;
}
}
i++
Any thoughts or suggestions on how I can get the foreach loop to go to the next item in the selected listbox?
Also, this is just a snippet as I have about 11 case items to loop through.
You probably want to switch on report, not reportname.
foreach(string item in listBox.Items)
{
}
?
Depends on how you setup the data source for the listbox though (I'm assuming this is WinForm?). If you created it by adding .Items or using the designer then this will work. However if you've used .DataSource then it wont work.
I'd personally have a
List<string> list = SomeMethodWhereIMakeTheList();
and set that to:
listbox.DataSource = list;
then I wouldn't even have to touch the ListBox to mess with the contents:
list.ForEach(...)
Don't do the print logic in a foreach. Split out the data then print such this (note I changed the name of reportname to reportnames to signify a list of items)
string reportnames = lstbxReports.SelectedValue.ToString();
var firstReport = reportnames.First(); // No error checking here, would use FirstOrDefault with null checks.
if (firstReport == "OverView")
PrintOverview(filename);
else
PrintSource(filename);
// Now print out the rest
reportnames.Skip(1)
.ToList()
.ForEach(rp =>
{
if (rp == "OverView")
PrintOverviewAppend(filename);
else
PrintSourceAppend(filename);
});
Even after the RemoveAt() method, my list keeps being the same and I don't even get an error:
foreach (var row in queryCandidates.ToList())
{
try
{
xString = queryCandidates.ToList().ElementAt(i).District;
int.TryParse(xString, out xNumber);
temp = xNumber.Equals(districtNumber);
System.Diagnostics.Debug.Write(temp+ " ");
System.Diagnostics.Debug.Write(i+" ");
if (temp == false)
{
System.Diagnostics.Debug.WriteLine(" i is:"+i);
//not working even when it should
queryCandidates.ToList().RemoveAt(i);
}
}
catch { }
i++;
if (last == i)
{
System.Diagnostics.Debug.WriteLine("before ending loop: ");
return View(queryCandidates.ToList());
}
}
System.Diagnostics.Debug.WriteLine("after ending the loop: ");
return View(queryCandidates.ToList());
ToList() creates a new instance. From this instance you are removing the element. You are not removing the element from the original enumerable.
You should be doing something like this instead:
var candidates = queryCandidates.ToList();
var elementsToRemove = new List<int>();
foreach (var row in candidates)
{
// ...
xString = candidates[i].District;
// ...
if (temp == false)
{
// ...
elementsToRemove.Add(i);
}
}
for(int i = elementsToRemove.Count - 1; i >= 0; --i)
candidates.RemoveAt(elementsToRemove[i]);
return View(candidates);
Please note the use of elementsToRemove. You can't remove the items directly in the loop. This will throw an exception.
Additionally, please note that ToList copies all data. Every single time you call it. It should be obvious that this is not a good idea to do in a loop.
queryCandidates.ToList().RemoveAt(i);
ToList() creates a brand new list, which you then remove an element from, but that list is long gone.
Try:
var newList = queryCandidates.ToList();
for (int i=newList.Count-1; i>=0; i--){
///snip
newList.RemoveAt(i);
Note that I changed your foreach to for (in reverse) because you cannot modify a list while you are iterating over it with foreach.
The ToList() function creates a new List every time you call it. The object is removed from that list, not from the original list. So you should call ToList once before the foreach.
Once you've done that the removeAt() call will work and cause new issues because then you are trying to modify the list from within the foreach loop. So you'll need to rewrite your code in a way which takes the remove out of the loop as well.
Well I'm not exactly sure what Type queryCandidates is, but the reason you are not seeing an update is because you are removing element 'i' from the wrong object. Your ToList() function creates a new object of List type. If you want to keep the change you need to cache that list and use it where you use your original queryCandidates object.
queryCandidates isn't a list.
You're converting it to a list which creates a new instance from which you're removing the item but doesn't affect queryCandidates itself.
You can do:
var queryCandidates myCollection.ToList();
and then
queryCandidates.RemoveAt(i);
What works for me is to remove from the bottom up:
for (int i = list.Count - 1; i > 0; i--)
{
if (list[i][0] == " " || list[i][3] == "0")
list.RemoveAt(i);
}
It makes sense that some items are missed after decreasing the item count.
I had learnt by reading your great answers here, that it is not good practice deleting items from within a foreach loop, as it is (and I quote) "Sawing off the branch you're sitting on".
My code currently removes the text from the dropdownlist, but the actual item remains (just without text displayed).
In other words, it isn't deleting, and probably can't because you can't delete from within a foreach loop.
After hours of trying I am unable to get my head around a way of doing it.
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
//this is the foreach loop
foreach (ToolStripItem mItem in favoritesToolStripMenuItem.DropDownItems)
{
//This rules out seperators
if (mItem is ToolStripMenuItem)
{
ToolStripMenuItem menuItem = mItem as ToolStripMenuItem;
//This matches the dropdownitems text to the CheckedItems String
if (((ToolStripMenuItem)mItem).Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
//And deletes the item
menuItem.DropDownItems.Remove(mItem);
}
}
}
}
But it isn't deleting because it is within a foreach loop!
I would greatly appreciate your help, and be truly amazed if anyone can get their head around this code :)
Kind Regards
Fun with LINQ!
// Loop through the checked items, same as you did.
foreach (var checkedItem in this.organizeFav.CheckedItems)
{
// Cast from IEnumerable to IEnumerable<T> so we can abuse LINQ
var matches = favoritesToolStripMenuItem.DropDownItems.Cast<ToolStripItem>()
// Only items that the Text match
.Where(item => item.Text == checkedItem.Text)
// Don't match separators
.Where(item => item is ToolStripMenuItem)
// Select the keys for the later .Remove call
.Select(item => item.Name);
// Loop through all matches
foreach (var key in matches)
{
// Remove them with the Remove(string key) overload.
favoritesToolStripMenuItem.Remove(key);
}
}
You don't need a foreach loop - just use a regular loop but go in reverse, start at the end and go to the beginning.
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
//this *replaces* the foreach loop
for(int j = favoritesToolStripMenuItem.DropDownItems.Count - 1; j >= 0; j--)
{
ToolStripMenuItem menuItem = favoritesToolStripMenuItem.DropDownItems[j] as ToolStripMenuItem;
//This rules out seperators
if (menuItem != null)
{
//This matches the dropdownitems text to the CheckedItems String
if (menuItem.Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
favoritesToolStripMenuItem.DropDownItems.Remove(menuItem);
}
}
}
}
this was #Kurresmack's code rearranged, i just coded it directly here in the page so excuse any small syntax error or anything obvious i overlooked (disclaimer: it is a sample!!)
You can still treat favoritesToolStripMenuItem.DropDownItems as a collection like you were, but you don't have to enumerate over it using a foreach. This cuts down on a few lines of code, and it works because you are iterating it in reverse order, you will not get an index out of bounds exception.
Try something like this:
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
List<ToolStripItem> toRemove = new List<ToolStripItem>();
//this is the foreach loop
foreach (ToolStripItem mItem in favoritesToolStripMenuItem.DropDownItems)
{
//This rules out seperators
if (mItem is ToolStripMenuItem)
{
ToolStripMenuItem menuItem = mItem as ToolStripMenuItem;
//This matches the dropdownitems text to the CheckedItems String
if (((ToolStripMenuItem)mItem).Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
toRemove.Add(mItem);
}
}
}
foreach(var item in toRemove)
{
favoritesToolStripMenuItem.DropDownItems.Remove(item);
}
}
To my mind, the way to make the code work is:
1. Create an instance of the type the favoritesToolStripMenuItem.DropDownItems collection is.
2. In the foreach loop, add all items, you do not want to be removed, to that collection.
3. Make favoritesToolStripMenuItem.DropDownItems to point to the new collection. Or clear favoritesToolStripMenuItem.DropDownItems and load the items from the new collection to it.
Hope this helps
Instead of a foreach use a reverse for-Loop:
for(int reverseIndex = myList.Count - 1; reverseIndex >= 0; reverseIndex--)
{
var currentItem = myList[reverseIndex];
if(MatchMyCondition(currentItem))
{
myList.Remove(currentItem);
}
}
I got this:
DataTable dtEntity = CreateDataTable();
drEntity = dtEntity.NewRow();
Then I add data to the row (or not).
Lots of code, really don't know if there's anything inside the row.
Depends on the input (i am importing from some files).
I'd like to do something like:
if (drEntity`s EVERY CELL IS NOT EMPTY)
{
dtEntity.Rows.Add(drEntity);
}
else
{
//don't add, will create a new one (drEntity = dtEntity.NewRow();)
}
Is there some nice way to check if the DataRow's every cell is empty?
Or I should foreach, and check them one by one?
A simple method along the lines of:
bool AreAllColumnsEmpty(DataRow dr)
{
if (dr == null)
{
return true;
}
else
{
foreach(var value in dr.ItemArray)
{
if (value != null)
{
return false;
}
}
return true;
}
}
Should give you what you're after, and to make it "nice" (as there's nothing as far as I'm aware, in the Framework), you could wrap it up as an extension method, and then your resultant code would be:
if (datarow.AreAllColumnsEmpty())
{
}
else
{
}
I created an extension method (gosh I wish Java had these) called IsEmpty as follows:
public static bool IsEmpty(this DataRow row)
{
return row == null || row.ItemArray.All(i => i is DBNull);
}
The other answers here are correct. I just felt mine lent brevity in its succinct use of Linq to Objects. BTW, this is really useful in conjunction with Excel parsing since users may tack on a row down the page (thousands of lines) with no regard to how that affects parsing the data.
In the same class, I put any other helpers I found useful, like parsers so that if the field contains text that you know should be a number, you can parse it fluently. Minor pro tip for anyone new to the idea. (Anyone at SO, really? Nah!)
With that in mind, here is an enhanced version:
public static bool IsEmpty(this DataRow row)
{
return row == null || row.ItemArray.All(i => i.IsNullEquivalent());
}
public static bool IsNullEquivalent(this object value)
{
return value == null
|| value is DBNull
|| string.IsNullOrWhiteSpace(value.ToString());
}
Now you have another useful helper, IsNullEquivalent which can be used in this context and any other, too. You could extend this to include things like "n/a" or "TBD" if you know that your data has placeholders like that.
I prefer approach of Tommy Carlier, but with a little change.
foreach (DataColumn column in row.Table.Columns)
if (!row.IsNull(column))
return false;
return true;
I suppose this approach looks more simple and cleaner.
public static bool AreAllCellsEmpty(DataRow row)
{
if (row == null) throw new ArgumentNullException("row");
for (int i = row.Table.Columns.Count - 1; i >= 0; i--)
if (!row.IsNull(i))
return false;
return true;
}
I know this has been answered already and it's an old question, but here's an extension method to do the same:
public static class DataExtensions
{
public static bool AreAllCellsEmpty(this DataRow row)
{
var itemArray = row.ItemArray;
if(itemArray==null)
return true;
return itemArray.All(x => string.IsNullOrWhiteSpace(x.ToString()));
}
}
And you use it like so:
if (dr.AreAllCellsEmpty())
// etc
You could use this:
if(drEntity.ItemArray.Where(c => IsNotEmpty(c)).ToArray().Length == 0)
{
// Row is empty
}
IsNotEmpty(cell) would be your own implementation, checking whether the data is null or empty, based on what type of data is in the cell. If it's a simple string, it could end up looking something like this:
if(drEntity.ItemArray.Where(c => c != null && !c.Equals("")).ToArray().Length == 0)
{
// Row is empty
}
else
{
// Row is not empty
}
Still, it essentially checks each cell for emptiness, and lets you know whether all cells in the row are empty.
DataTable.NewRow will initialize each field to:
the default value for each DataColumn (DataColumn.DefaultValue)
except for auto-increment columns (DataColumn.AutoIncrement == true), which will be initialized to the next auto-increment value.
and expression columns (DataColumn.Expression.Length > 0) are also a special case; the default value will depend on the default values of columns on which the expression is calculated.
So you should probably be checking something like:
bool isDirty = false;
for (int i=0; i<table.Columns.Count; i++)
{
if (table.Columns[i].Expression.Length > 0) continue;
if (table.Columns[i].AutoIncrement) continue;
if (row[i] != table.Columns[i].DefaultValue) isDirty = true;
}
I'll leave the LINQ version as an exercise :)
AFAIK, there is no method that does this in the framework. Even if there was support for something like this in the framework, it would essentially be doing the same thing. And that would be looking at each cell in the DataRow to see if it is empty.
I did it like this:
var listOfRows = new List<DataRow>();
foreach (var row in resultTable.Rows.Cast<DataRow>())
{
var isEmpty = row.ItemArray.All(x => x == null || (x!= null && string.IsNullOrWhiteSpace(x.ToString())));
if (!isEmpty)
{
listOfRows.Add(row);
}
}
Maybe a better solution would be to add an extra column that is automatically set to 1 on each row. As soon as there is an element that is not null change it to a 0.
then
If(drEntitity.rows[i].coulmn[8] = 1)
{
dtEntity.Rows.Add(drEntity);
}
else
{
//don't add, will create a new one (drEntity = dtEntity.NewRow();)
}
To delete null and also empty entries Try this
foreach (var column in drEntitity.Columns.Cast<DataColumn>().ToArray())
{
if (drEntitity.AsEnumerable().All(dr => dr.IsNull(column) | string.IsNullOrEmpty( dr[column].ToString())))
drEntitity.Columns.Remove(column);
}
I have to delete some rows from a data table. I've heard that it is not ok to change a collection while iterating through it. So instead of a for loop in which I check if a row meets the demands for deletion and then mark it as deleted, I should first iterate through the data table and add all of the rows in a list, then iterate through the list and mark the rows for deletions. What are the reasons for this, and what alternatives do I have (instead of using the rows list I mean)?.
Iterating Backwards through the List sounds like a better approach, because if you remove an element and other elements "fall into the gap", that does not matter because you have already looked at those. Also, you do not have to worry about your counter variable becoming larger than the .Count.
List<int> test = new List<int>();
test.Add(1);
test.Add(2);
test.Add(3);
test.Add(4);
test.Add(5);
test.Add(6);
test.Add(7);
test.Add(8);
for (int i = test.Count-1; i > -1; i--)
{
if(someCondition){
test.RemoveAt(i);
}
}
Taking #bruno code, I'd do it backwards.
Because when you move backwards, the missing array indices do not interfere with the order of your loop.
var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });
for (int i = l.Count - 1; i >= 0; i--)
if (l[i] % 2 == 0)
l.RemoveAt(i);
foreach (var i in l)
{
Console.WriteLine(i);
}
But seriuosly, these days, I'd use LINQ:
var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });
l.RemoveAll(n => n % 2 == 0);
You can remove elements from a collection if you use a simple for loop.
Take a look at this example:
var l = new List<int>();
l.Add(0);
l.Add(1);
l.Add(2);
l.Add(3);
l.Add(4);
l.Add(5);
l.Add(6);
for (int i = 0; i < l.Count; i++)
{
if (l[i] % 2 == 0)
{
l.RemoveAt(i);
i--;
}
}
foreach (var i in l)
{
Console.WriteLine(i);
}
Since you're working with a DataTable and need to be able to persist any changes back to the server with a table adapter (see comments), here is an example of how you should delete rows:
DataTable dt;
// remove all rows where the last name starts with "B"
foreach (DataRow row in dt.Rows)
{
if (row["LASTNAME"].ToString().StartsWith("B"))
{
// mark the row for deletion:
row.Delete();
}
}
Calling delete on the rows will change their RowState property to Deleted, but leave the deleted rows in the table. If you still need to work with this table before persisting changes back to the server (like if you want to display the table's contents minus the deleted rows), you need to check the RowState of each row as you're iterating through it like this:
foreach (DataRow row in dt.Rows)
{
if (row.RowState != DataRowState.Deleted)
{
// this row has not been deleted - go ahead and show it
}
}
Removing rows from the collection (as in bruno's answer) will break the table adapter, and should generally not be done with a DataTable.
A while loop would handle this:
int i = 0;
while(i < list.Count)
{
if(<codition for removing element met>)
{
list.RemoveAt(i);
}
else
{
i++;
}
}
chakrit's solution can also be used if you are targetting .NET 2.0 (no LINQ/lambda expressions) by using a delegate rather than a lambda expression:
public bool IsMatch(int item) {
return (item % 3 == 1); // put whatever condition you want here
}
public void RemoveMatching() {
List<int> x = new List<int>();
x.RemoveAll(new Predicate<int>(IsMatch));
}
Deleting or adding to the list whilst iterating through it can break it, like you said.
I often used a two list approach to solve the problem:
ArrayList matches = new ArrayList(); //second list
for MyObject obj in my_list
{
if (obj.property == value_i_care_about)
matches.addLast(obj);
}
//now modify
for MyObject m in matches
{
my_list.remove(m); //use second list to delete from first list
}
//finished.
When I need to remove an item from a collection that I am enumerating I usually enumerate it in reverse.