It is my first time here and I am struggling to solve this issue.
I have this piece of code:
try
{
progressBar1.Maximum = lista.Items.Count;
lista.BeginUpdate();
for (int i = 0; lista.Items.Count > i; i++)
//for (int i = lista.Items.Count - 1; -1 < i; i--)
{
if (lista.Items[i].SubItems[1].Text.ToLower().Contains(Text) == false)
{
lista.Items[i].Remove();
}
progressBar1.Value = progressBar1.Value + 1;
}
lista.EndUpdate();
progressBar1.Value = 0;
}
catch (Exception errore)
{
txt_info.Text = "" + errore.Message;
progressBar1.Value = 0;
}
The method lista.items[i].remove is extremely slow.
lista is a ListView and I am working on a log file bigger than 50,000 lines.
Is there anyway to speed up the process?
I would take a different approach and use LINQ, something like this :
lista.Items = lista.Items.Where(x=>x.SubItems[1].Text.ToLower.Contains(Text)).AsParallel().ToList();
Basically, rebuilding the list once rather than trying to remove individual items over and over again.
ListViewItem[] allElements = new ListViewItem[listView1.Items.Count];
listView1.Items.CopyTo(allElements, 0);
List < ListViewItem > list = allElements.ToList();
list.RemoveAll(item => item.SubItems[1].Text.ToLower().Contains(TextToFind) == false);
listView1.BeginUpdate();
listView1.Clear();
listView1.Items.AddRange(list.ToArray());
listView1.EndUpdate();
First Rule is never update list in for loop. Your logic will only run till half of the list. I guess that's not what you want.
I've seen that manipulating listview.items is very slow even after using BeginUpdate and EndUpdate. Key is to do the manipulation outside (in list or so) and then re populate the list with AddRange (which is much faster than Add).
The simplest option would be to use the list's own RemoveAll method.
list.RemoveAll(x => !x.SubItems[1].Text.ToLower().Contains(Text))
P.S.
You might want to look for speed gains in the actual comparison.
Using String.Compare is much faster if your requirement fits it. If you want to check for a sub-string, I would suggest using ToUpperInvariant for invariance related matters - it's designed to be faster.
You could stick it in a background worker and have it do this on it's own. Therefore your users could still use the program while this process is occuring.
If you remove an item in for loop, you should set the counter to 1 less so you won't miss one. Because you remove [i], [old i+1] becomes the [new i] (Items.Count decreases 1 also) and you will miss checking the [new i].
e.g:
ListView.Items.Remove[i];
i--;
Lists are slow to access dynamically and much better suited to iteration. I would suggest manipulating the data outside of the listview (maybe by copying the rows you want to display into a temporary list one at a time as you iterate across the source collection) and then assigning to the listview at the end. This can be done on a different thread (to improve UI performance) and doesn't require expensive lookups.
Related
So, if I want to draw a graph on a 500pixel canvas, I can't do much with 200.000 coordinates to draw lines. How can I delete 99% of this list, while still roughly keeping the trend of the data?
My Idea was:
for(int i = 0; i<200000; i++)
{
if(i%2000 == 0)
{
newList.Add(List[i])
}
}
I am just not sure, if this is the most efficient way to do this. I know that certain trends might get lost in this, but It should still be good enough (unless there is a more efficient, faster algorithm)
There is the Ramer–Douglas–Peucker algorithm, which is overkill I think, and also not really needed or even too inefficient for what I need.
Rather than enumerating over each and only taking the 2000th element May I suggest skipping over all these values and only taking the nth element? That way it should complete the for loop much faster.
Another option is to average the 2000 points and add that as a single point.
I've included both approaches below:
var data = new List<float>(200000);
var segmentLength = 2000;
var newList = new List<float>();
// Sampling, this is your idea, without enumerating over every single element
for (var i=0; i<data.Count(); i+=segmentLength)
{
newList.Add(data[i]);
}
// Averaging
for (var i=0; i<data.Count(); i+=segmentLength)
{
newList.Add(data.GetRange(i*segmentLength, segmentLength).Average())
}
I've got a situation where I'm using an ArrayList to store a list of file names (full file path). When I add multiple items of the same file to the array, then use ArrayList.IndexOf to find the index (I'm reporting progress to a BackgroundWorker), it always returns the index of the first item since it's searching by the file name. This causes a problem with a progress bar, i.e. I'm processing 3 files, and when complete, the bar is only 1/3 full.
Here's some sample code (I'm simply adding items here, but in the real code they are added from a ListBox):
ArrayList list = new ArrayList();
list.Add("C:\Test\TestFile1.txt");
list.Add("C:\Test\TestFile1.txt");
list.Add("C:\Test\TestFile1.txt");
var files = list;
foreach (string file in files)
backgroundWorker1.ReportProgress(files.IndexOf(file) + 1);
When this runs, only 1 "percent" of progress is reported, since the IndexOf is finding the same file every time. Is there any way around this? Or does anyone have a suggestion on another way to get the index (or any unique identifier for each item)?
The simplest approach is just to use the index to iterate:
for (int i = 0; i < list.Count; i++)
{
backgroundWorks.ReportProgress(i + 1);
// Do stuff with list[i]
}
To achieve what you want, I would recomment using a for list. You won't have to search for any indexes and can report the progress easily to the backgroundWorker1:
for (int counter = 0; counter < files.Count; counter++)
{
backgroundWorker1.ReportProgress(counter + 1);
}
By doing this, you don't get problems with same filenames.
This would be the equivalent with foreach:
int counter = 1;
foreach (string file in files)
{
backgroundWorker1.ReportProgress(counter);
counter++;
}
But I think it's better to use for in this case.
You could make a copy of your list, and then remove each item from it as it is processed. Then you could return the percentage complete as a function of the count of the temp list divided by the count of the original list.
You could also just add to an int every time your background workers process a file and use that int to track the progress (percent complete should be the int divided by the number of files).
Of course with either approach you need to make sure you are modifying the variables in question in a thread-safe manner.
Try this and it works fine for me in case of reading files having so many lines of data.
int percentage = (int)((count / (double)lineCount) * 100.0);
backgroundWorker5.ReportProgress(percentage);
Here count is total count and linecount is present running count. By using thedse two,calculate the percentage. Keep this code in your for loop or while loop.
Assuming that progressbar max value is 100, and with one file processed out of three it should show 33(%) progress, you could count progress for each file processed and recalculate overall progress :
// list contains x file paths
var files = list;
double progressStep = 1 / files.Count;
for(int i = 0; i < files.Count; i++)
{
backgroundWorker1.ReportProgress((i + 1) * progressStep);
...
}
All that remained is to assign progress value in background worker to progress bar.
I want to have the best performance and I know that array its faster than list but with array I need to create a variable for counter and even may need to use .Count or .Length to find the size so I though maybe better just use list? Below are the examples.
Example 1:
foreach (var item in items)
ItemCollection.Add(item);
Example 2:
int i = 0;
foreach (var item in items)
{
ItemCollection[i] = item;
i++;
}
Example 3:
for (int i = 0; i < items.Count; i++)
ItemCollection[i] = item;
Example one is your best option as it appears you are trying to dynamically change the size of your array/list,
Example two is just silly.
And example 3 would become tricky when you wish to extend the array. See my first point
A point to note in your third example is in your for loop you have
for (int i = 0; i < items.Count; i++)
This will revaluate items.Count every iteration so you could micro-optimize by moving this out of the for loop
var length = items.Count
for (int i = 0; i < length; i++)
Performance of a list is nearly identical to that of an array. If you know the exact number of items that you are planning to add, you can eliminate the potential memory overhead as well by creating a list with the exact number of elements to avoid re-allocations on Add:
// Reserve the required number of spots in the list
var ItemCollection = new List<ItemType>(items.Count);
foreach (var item in items)
// Add is not going to cause reallocation,
// because we reserved enough space ahead of time
ItemCollection.Add(item);
In most instances, this turns out to be a premature micro-optimization.
Well you can use 'foreach' on Arrays :
int[] bob = new int[] { 0, 1, 2, 3 };
foreach (int i in bob)
{
Console.WriteLine(i);
}
Anyway, in most cases, the difference should be pretty negligible. You also have to realize that 'foreach' doesn't magically iterate through the list, it calls 'GetEnumerator' and then uses this to loop, which also uses some ram (actually more than just creating 'int i').
I generally use Arrays when I know the length is fixed and will remain pretty small, otherwise using Lists is just a lot easier.
Also, don't optimize until you know you need to, you're pretty much wasting your time otherwise.
I'm making a Black Jack game, and at the start of every new round I need to clear the list of cards that represents the Player's and the Dealer's hands. I used this to do so:
public void ClearPlayerHand()
{
for (int i = 0; i < PlayerHand.Count; ++i)
{
PlayerHand.Remove(PlayerHand[i]);
}
}
Problem is I always seem to be left with one card left in the list, or I receive an out of bounds error, no matter how I change the value of i, what is the best method of removing all the elements from the PlayerHand?
If your collection PlayerHand implements ICollection<T> you can just call the .Clear() method.
A common implementation of this interface is List<T>.
If you do want to clear a List<T> via a for loop, you should use a reverse for loop. The reason for this is that as you remove an item from the list, it will shift all the index's down one, and you could easy run into index out of bounds exceptions.
An example of this would be:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
The other answers are right: use Clear.
But, if you wanted to do this with a loop and Remove calls, here's how you would do it:
for(int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
Reversing the direction of the iteration is the real trick.
This is the best/easiest way to do it.
PlayerHand.Clear();
Reason for out of bounds
As for why you are receiving the out of bounds exception, it's happening because you're removing elements from the list but continually counting up. You would want the last operation to remove i = 0 but it keeps counting.
Say PlayerHand has 3 items in it, the following occurs:
i = 0
remove PlayerHand[0] (it now contains 2 elements)
i = 1
remove PlayerHand[1] (it now contains 1 element)
i = 2
remove PlayerHand[2] (this throws an exception as only PlayerHand[0] exists)
Normally you would count backwards in this case:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
Alternatively, you can consider using data binding and then you should update the ItemSource, instead of directly manipulating the listbox or listview items.
List<T> SomeSource=...
PlayHand.ItemSource=SomeSource;
SomeSource.Clear();
Another suggested approach beside Clear method, you can also use RemoveAll to either remove all or part of list
// Remove all items
PlayerHand.RemoveAll(x => true);
// Remove part of list
PlayerHand.RemoveAll(x => ConditionMethod(x));
I was facing this problem earlier today, and since I could not find a satisfactory solution, I decided to change my class design, and have seperate properties such as Tag 1, Tag 2, Tag 3 etc.
My main problem is the fact that I need to bind a grid to an object that contains a list among other properties and I need to show each item in the list as a separate column which I am unable to do. Hence I am resorting to declaring variables separately. Original question is here...
Now, I'm facing one of the most common design problem that probably every programmer has at some point of time. Here is the code to demonstrate it,
for (int i = 0; i < tags.Length; ++i) // Length not known here.
{
if(i==0){
tag1 = tags[0];
}
else if(i == 1){
tag2 = tags[1];
}
else if(i == 2){
tag3 = tags[2];
}
....
}
Here tags is a string array.
I was wondering if there is a more elegant way to do this. Another thing to note is that the efficiency of this loop decreases as it progresses, since with more iterations it has to check more conditions. If we could remove a condition after it had become true once it would speed up each iteration since we know that each condition will become true only once in all the iterations
Moved answer about DataGridView and using ComponentModel to the correct question:
Displaying a list of object containing a list in a grid view
Briefing
The DataGridView controll supports the ComponentModel namespace so that you can create classes that appear to have properties that don't exist. It is the same mechanism the PropertyGrid uses.
The sample code is in this answer of that question:
https://stackoverflow.com/a/13078735/195417
OLD ANSWER
This was my previous answer, when I didn't realize the real question was about the DataGridView control.
Isn't this the same as setting the values directly:
this.tag1 = tags[0];
this.tag2 = tags[1];
this.tag3 = tags[2];
EDIT: as you sayd you don't know how many variables will be needed, then you need only one, and that is a list:
var list = new List<string>();
for (int i = 0; i < tags.Length; ++i)
{
list.add(tags[i]);
}
If all you want is to copy all values, you can even do this:
var list = new List<string>(tags);
Tell me whether this is what you want or not... maybe I have misunderstood the question.
The whole loop is pointless. But unless the tags array length is always going to be the same, you have to be sure not to go out of bounds...
if(tags.Length >= 1) this.tag1 = tags[0];
if(tags.Length >= 2) this.tag2 = tags[1];
if(tags.Length >= 3) this.tag3 = tags[2];
if(tags.Length >= 4) this.tag4 = tags[3];
if(tags.Length >= 5) this.tag5 = tags[4];
... so on for however many this.tag# you have.
This is essentially the same:
for(int index = 0; index < tags.Length[]; index++){
switch(index){
case 0:
tag1 = tags[0];
break;
// And so on
}
}