I have a list of items, lets say 100 items. I need to add another element before the existing element that matches my condition. What is the fastest way and the most performance optimized to do this?
ie.:
foreach (var i in myList)
{
if (myList[i].value == "myValue")
{
myList[i-1] add ("someOtherValue")
}
}
Maybe i should use other container?
First you could find the index of your item using FindIndex method:
var index = myList.FindIndex(x => x.value == "myvalue");
Then Insert at the right point:
myList.Insert(index,newItem);
Note that inserting at a given index pushes everything else forward (think about finding your item at index 0).
Consider using a LinkedList<T>. It has the advantage that inserting or removing items does not require shifting any items. The disadvantage is that items cannot be accessed randomly. You have to traverse the list starting at the first or last item in order to access the items.
myList.Insert(myList.IndexOf("myValue") - 1, "someOtherValue");
You should probably check to make sure myvalue exists first, and it is not in index 0.
int index = myList.IndexOf("myValue");
if (index >= 0)
myList.Insert(index, "myNewValue");
By the way, you should not modify your own collection or list while iterating with for-each (as in your code above).
I presume the list is an array - in which case have you tried doing this with Linq?
string[] mylist = new string[100];
// init the list
List<string> list = keys.ToList();
list.Insert(1,"somethingelse");
mylist = list.ToArray(); // convert back to array if required
if it is a List to begin with, you can skip the conversions and use Insert directly.
Related
I have a List of string values and and some of the values contains xxx or XXX in front.
xxxRed
xxxYellow
xxxxCareful with that axe Eugene!
xxxxxxdedicum aceasta frumoasa Melodia
xxxxLeaders
xxxxWorking Around - titles
XXXXXNothing To Fear
xxxxAvoiding standards
xxxFirst Aid
List<string> lstTitles = new List<string>();
This is what I have tried
for (int i=0; i < lstTitles.Count; i++)
{
string title = lstTitles[i].ToLower().Trim();
if (title[0] == 'x')
{
lstTitles.Remove(lstTitles[i]);
}
}
Problem I have is that only some of the values are removed but not all of them.
Is there perhaps a better way of removing these values?
Use RemoveAll method
lstTitles.RemoveAll(s => s[0] == 'x' || s[0] == 'X');
and you may want to use StartsWith instead of comparing first char.
lstTitles.RemoveAll(s => s.StartsWith("x",StringComparison.InvariantCultureIgnoreCase));
Problem I have is that Only some of the values are removed but not all of them.
Because you're skipping items. When you call Remove(), the next item will be at index i, but you'll increase i in the next loop.
It can be solved by iterating over a copy of the list, and removing unwanted items in the original:
foreach (var item in lstTitles.ToList())
{
if (item.StartsWith("x", StringComparison.InvariantCultureIgnoreCase))
{
lstTitles.Remove(item);
}
}
Though this involves creating a copy of the list, which isn't really useful, as well as calling Remove() which itself is far from performant.
So you could invert your for-loop, to remove the last items first which doesn't change the indexing for unprocessed items:
for (int i = lstTitles.Count - 1; i > 0; i--)
{
if (lstTitles[i].StartsWith("x", StringComparison.InvariantCultureIgnoreCase))
{
lstTitles.RemoveAt(i);
}
}
But as #I4V points out, all of this logic already is in List<T>.RemoveAll(), which is nicer to read and probably optimized for some edge cases, so there's little use to hand-code it again.
That's because your skipping values.
Suppose your list contains ['xVal1', 'xVal2', 'val3', 'xVal4', 'val5']. At first your i is 0, and you look at list[0], which is 'xVal1', so you remove it.
Now your list contains ['xVal2', 'val3', 'xVal4', 'val5'], and your i is 1. So you look at list[1] which is 'val3'. You ignored xVal2 !
You can start at the back of the list and go to the front, although you will still have a potential bug in case there are identical values you remove.
A shorter way would be to use LINQ:
var newList = lstTitles.Where(title=>!title.StartsWith('xxx'))
Instead of ToLower you should use the overload of StartsWith which allows to pass a StringComparison.OrdinalIgnoreCase.
Then use List.RemoveAll which is the most readable, most efficient and shortest approach:
lstTitles.RemoveAll(s => s.TrimStart().StartsWith("x", StringComparison.OrdinalIgnoreCase));
Demo
I think, you'd better just create a new list this way
list = list
.Where(i => ! i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase))
.ToList();
It would have a O(n) complexity whereas, trying to remove then 1 by 1 would be in O(n^2).
This could work also :
list.RemoveAll(i => i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase));
Handles all cases and without a second list.
I am looking for a way to remove a set of indexes from a list AT ONCE. By this i mean, I have a list of indices for ex, 0,1,5,7 that i want to remove from a string list of animals. I need a way to remove all the animals at the indices 0,1,5,7 in one shot. THis is because if i iterate through the animals list and delete the animal at 0,1,5,7 at each iteration, the list structure changes and the indeces no longer apply to the updated animal list.
Thanks
Well, one option is to delete them starting with the highest one instead. For example:
foreach (var index in indexesToDelete.OrderByDescending(x => x))
{
list.RemoveAt(index);
}
For example, removing item 5 doesn't affect items 0-4.
In addition to what #Jon Skeet said, another option is to copy the list, leaving out the items you don't want.
List<string> copy = new List<string>(list.Count - indexesToDelete.Count);
for (int index = 0; index < list.Count; index++)
{
if (!indexesToDelete.Contains(index))
copy.Add(list[index]);
}
list = copy;
I am not claiming this is an improvement over Jon's answer, only that it is an alternative. I can imagine some scenerios where this approach may be preferred.
I have a collection< of int's >.
1
2
3
When i remove, for example, the 2. The collection becomes 1,3.
However, when I go to add another item the list becomes
1
3
3
Because the sequence is based off the count of items in collection
is there an easy way to resequence. The example above where i'm showing 1,3 should be resequence to 1,2 and then the next new item will be a 3.
It sounds like your list can be replaced with a integer variable, since all you are "storing" here is the (1-based) index of the item in the list. Creating a "list" from 1 to n would just be int count = n. Testing if an item is in the list becomes if (item <= count). Adding an item would be count++, and removing count--.
You could rewrite your list every time you remove an object using Enumerable.Range, so the code to you would be something like this:
//execute this after remove element
void Resequence()
{
myList = Enumerable.Range(1, myList.Count).ToList();
}
so the add will be something like this
void AddElement(){
myList.Add(myList.Count + 1);
}
Note: in the add element you should remove the +1 if the list is zeroBased.
I have a problem with the following. I have a collection:
Collection<Vehicle> list = new Collection<Vehicle>();
code = 1,2,3, Description = "aaa"
code = 10,438,13, Description = "bbb"
code = 81,8623,362, Description = "ccc"
code = 163,4312,53, Description = "ddd"
...
But I only care about some of them.. The list I care about is here, i.e. codesCareAbout = "1|2|3|163|4312|53"
I need to iterate through the collection and either deleting the Vehicle I don't care about, or cherry picking into another list containing the Vehicles I care about?
Any suggestions?
Many thanks,
James
You can iterate your list backwards, and use RemoveAt using the for index to remove from the list:
for (int i = list.Count - 1; i >= 0; i--)
{
Foo item = list[i];
if (IsFoobar(item))
list.RemoveAt(i);
}
Counting backwards is required so that you don't mutate your index counting as you go, using a for loop is required because you cannot mutate a list being enumerated with an enumerator.
Alternatively, do as you suggested, populate into an empty list the stuff you want - however, usage depends on whether you need to modify the list you are given or can make a new list.
Assuming that Vehicule has Code (string) property and Description property (question is not clear !).
1/ Clone the list : var clone = list.ToList();
2/ Iterate and decide if current item is interesting :
foreach(var item in clone)
{
if (! IsInteresting(item))
list.Remove(item);
}
3/ IsInteresting could be, for example :
foreach(var code in item.Code.Split(','))
{
if (codesCareAbout.Split('|').Contains(code))
return true;
}
Filtering the list with linq produces the cleanest code:
var interestinglist = list.Where(v=> v.IsInteresting(v));
IsInteresting can be implemented as
codesIcareAbout.Intersect(v.Codes).Any();
this assumes both fields are collections rather than strings, but that is easily fixed with a call to string.Split().
Let's say there is a list of List<UInt32>
Thus, :
12|12
23|33
33|22
11|22
I need to remove 0th and 2nd element (List<UInt32>). However, when I try to foreach this list and first remove 0th, the List collapses its elements and 1st becomes 0th now.. so I don't want to delete the wrong element, because my another List<int> includes the positions of elements I want to delete.
Anyway, I thought of doing some algorithm for this, but I wonder if there is already solution for this problem.
Sort the positions list in descending order and remove elements in that order.
foreach (var position in positions.OrderByDescending(x=>x))
list.RemoveAt(position);
Are you able to tell which elements you need to remove based on the elements themselves instead of the index? If so, and you want to change the existing list instead of creating a newly filtered one, use:
list.RemoveAll(x => (whatever - a predicate));
This is more efficient than removing elements one at a time - each element is only moved once. It's also clearer, if the predicate is obvious :)
If you are using C#3.0 (or greater) you can use the LINQ extension method Where to filter your list.
var source = new List<int> { 1, 2, 3, 4, 5 };
var filter = new List<int> { 0, 2 };
var result = source.Where((n, i) => !filter.Contains(i)) // 2, 4, 5
And if you want it back to a List<int> instead of IEnumerable<int>, just use ToList()
var list = result.ToList();
You could create a new collection and adding the items that you don't want to remove and then assign the new collection to the original. That may even be faster than RemoveAt if the indices to be removed are in a hashset/dictionary rather than a list.