I have
var a = new object();var b = new object();
var c = new object();
var d = new object();
var e = new object();
var list = new List<object> {a, b, c, d, e, a, d};
Then I need to remove last two objects (a
and d
), because they already in the list.
How I can do it without creating new List?
If you call list.Remove
it will remove the first instance; you can, however, use list.RemoveAt
and list.RemoveRange
. For example:
list.RemoveRange(5,2);
or better: don't add them in the first place.
For example, if you are adding from a sequence you can use Distinct
or HashSet<T>
to find unique items when adding.
After the fact, you could use:
var dups = from item in list
group item by item into grp
where grp.Count() > 1
select grp.Key;
foreach (var val in dups)
{
int first = list.IndexOf(val), last;
while ((last = list.LastIndexOf(val)) != first)
{
list.RemoveAt(last);
}
}
To remove all but the first instance of duplicates.
Or perhaps more efficiently:
for (int i = 0; i < list.Count; i++ )
{
var val = list[i];
int first = list.IndexOf(val), last;
while ((last = list.LastIndexOf(val)) != first)
{
list.RemoveAt(last);
}
}
You could use this backwards for-loop and Enumerable.Contains
+ List.RemoveAt
. This will not create a new list:
var list = new List<object> { "a", "b", "c", "d", "e", "a", "d" };
for (int i = list.Count - 1; i >= 0; i--)
{
var obj = list[i];
if(list.Take(i).Contains(obj))
list.RemoveAt(i);
}
So this loop gets one object via indexer, takes all objects before it's index and compares it with each other. Note that Take
doesn't create a collection due to it's deferred execution, understood it as a loop, it'll end as soon as Contains
returns true
. If it's already available it will be removed from the list. So the early bird gets the worm.
However, if you want to support custom types you need to override Equals
and GetHashCode
in your class otherwise Contains
will just compare the references.
Demo
How I can do it without creating new List?
You requirement is somewhat surprising (or at least you didn't explain why it is important that the list has to be modified in place), but here is a solution that favors speed of memory usage:
var set = new HashSet<object>();
var indicesToRemove = new List<int>();
for (var i = 0; i < list.Count; ++i) {
var item = list[i];
if (!set.Contains(item))
set.Add(item);
else
indicesToRemove.Add(i);
}
var itemsRemovedSoFar = 0;
foreach (var i in indicesToRemove) {
list.RemoveAt(i - itemsRemovedSoFar);
itemsRemovedSoFar += 1;
}
Compare this to a solution where a new list is created:
var list = list.Distinct().ToList();
I certainly prefer the second solution but it doesn't satisfy you requirement.