Removing element from list with predicate - c#

I have a list from the .NET collections library and I want to remove a single element. Sadly, I cannot find it by comparing directly with another object.
I fear that using FindIndex and RemoveAt will cause multiple traversals of the list.
I don't know how to use Enumerators to remove elements, otherwise that could have worked.
RemoveAll does what I need, but will not stop after one element is found.
Ideas?

List<T> has a FindIndex method that accepts a predicate
int index = words.FindIndex(s => s.StartsWith("x"));
if (index >= 0)
{
words.RemoveAt(index);
}
Removes first word starting with "x". words is assumed to be a List<string> in this example.

If you want to remove only the first element that matches a predicate you can use the following (example):
List<int> list = new List<int>();
list.Remove(list.FirstOrDefault(x => x = 10));
where (x => x = 10) is obviously your predicate for matching the objects.

EDIT: Now the OP has changed to use a LinkedList<T>, it's easy to give an answer which only iterates as far as it has to:
public static void RemoveFirst<T>(LinkedList<T> list, Predicate<T> predicate)
{
var node = list.First;
while (node != null)
{
if (predicate(node.Value))
{
list.Remove(node);
return;
}
node = node.Next;
}
}

In case someone need same thing, but for IList<T>
(Inspired by Strillo answer, but more efficient)
public bool Remove(this IList<T> list, Predicate<T> predicate)
{
for(int i = 0; i < list.Count; i++)
{
if(predicate(list[i]))
{
list.RemoveAt(i);
return true;
}
}
return false;
}

Related

How to replace an element in a Collection

The thing I wanna do would appear really simple - I want to find an element in an ICollection<T> that satisfies a given predicate and replace it with another. In C++ I would write this like:
for(auto &element : collection) {
if(predicate(elem)) {
element = newElement;
}
}
Grab the element by reference and reassign it. However doing
foreach(ref var element in collection)
in C# fails to compile, and I'm unsure if it'd even do what I want if it did compile. How do I access the physical reference within a collection to modify it?
My method signature if it helps:
public static void ReplaceReference<T>(
ICollection<T> collection,
T newReference,
Func<T, bool> predicate)
EDIT:
Since it appears unclear, I cannot just take the ICollection<T> and change it to something else. I'm getting an ICollection - that's all I know and I can't change that. No matter how much I'd love this to be an IList, or IEasilyReplacable I can't influence that.
ICollection<T> wouldn't be the best for this scenario. IList<T> allows you to assign with the indexer.
Another option would be to create a new collection as you iterate.
You could also write some sort of wrapper that is the actual reference in the collection and holds the value:
ICollection<Wrapper<T>> collection = ...;
foreach(var wrapper in collection)
{
wrapper.Value = newValue;
}
As per my understanding you want to replace specific item in collection based on given predicate, I tried below code and it is works fine for me.
I've created a list of string with 4 items and i asked my generic method to search for string with value "Name 1" if it is true it should change it to value "Name 5".
I've tested it using console application so you can test it by creating forloop that show values of list using Console.WriteLine();
public void Main(string[] args)
{
List<string> list = new List<string>();
list.Add("Name 1");
list.Add("Name 2");
list.Add("Name 3");
list.Add("Name 4");
Func<string, bool> logicFunc = (listItemValue) => listItemValue == "Name 1";
ReplaceReference(list, "Name 5", logicFunc);
}
public static void ReplaceReference<T>(ICollection<T> collection, T newReference, Func<T, bool> predicate)
{
var typeName = typeof(T).Name;
var newCollection = collection.ToList();
for (int i = 0; i < newCollection.Count; i++)
{
if (predicate(newCollection[i]))
{
newCollection[i] = newReference;
}
}
}
So I bashed my head against the wall and came up with a really simple solution for the particular replace problem, which is to find, remove and then add.
var existing = collection.FirstOrDefault(predicate);
if (existing != null)
{
collection.Remove(existing);
collection.Add(newReference);
}
However, I see it as rather a workaround to my foreach issue, and have thus posted this question as a follow-up: Grab element from a Collection by reference in a foreach
EDIT:
For Daniel A. White's comment:
Handling only the first one was what I intended to do, but it can be easily changed to replace-all:
var existing = collection.Where(predicate);
foreach(var element in existing)
{
collection.Remove(element);
}
for(int i = 0; i < existing.Count); ++i)
{
collection.Add(newReference);
}
As for ordering - ICollection is not necessarily ordered. So the way for fixing that would be creating a new method with a less general signature
static void ReplaceReference<T>(
IList<T> list,
T newReference,
Func<T, bool> predicate)
that would use the indexer to replace the values
for(int i = 0; i < list.Count; ++i)
{
if(predicate(list[i]))
{
list[i] = newReference;
// break here if replace-one variant.
}
}
And now in the main method we check if our collection is an IList, therefore ordered, and pass it to the ordered version:
if(collection is IList<T> list)
{
ReplaceReference(list, newReference, predicate);
return;
}
===========================================================================
Sidenote: of course there is also the dumbo approach:
var newCollection = new List<T>();
foreach(var element in collection)
{
newList.Add(predicate(element) ? newReference : element);
}
collection.Clear();
foreach(var newElement in newCollection)
{
collection.Add(newElement);
}
but it's highly inefficient.

Efficient sorting when you only want the best

I have a list of 5000 items that are sorted using a custom algoritm.
I only need the best one, i.e. list[0] after the sort has completed.
So I need an algoritm that takes the first item of the list, compares it to the second item and then compares the better one of these two, with the third item etc. Just one loop through the whole list (order n).
Which sorting method in c# should I use for this rather common scenario?
I believe the Sort(..) algoritm that I currently use is very inefficient for this purpose.
You can use MoreLINQ's MaxBy() for this.
Depending on how you define "best", the selector parameter you specify might be different.
Example: You have a list of strings and the "best value" is the longest string.
string longestString = listOfStrings.MaxBy(x => x.Length);
As you can see from the linked implementation, this is O(n). This is the best that is possible for an unsorted set.
I found the MaxBy of MoreLinq with selection, projection, source and key somewhat too complicated and just used my own code as an extension method:
public static T GetBest<T>(this List<T> list, IComparer<T> comparer)
{
if (list == null) throw new ArgumentNullException("list");
if (comparer == null) throw new ArgumentNullException("comparer");
if (list.Count > 0)
{
T best = list[0];
for (int i = 1; i < list.Count; i++)
{
if (comparer.Compare(best, list[i]) > 0)
{
best = list[i];
}
}
return best;
}
return default(T);
}

what is the most performant way to delete elements from a List<T> in c# while doing a foreach?

i want to know what is the best way to delete elements from a List in c# while doing a foreach.
here is a code sample. first i create a list with some elements and then delte one:
List<int> foo = new List<int>();
foo.Add(1);
foo.Add(2);
foo.Add(3);
foreach (int i in foo)
{
if (i==2)
{
foo.Remove(i);
}
}
when i run this, i get a InvalidOperationException but how to solve this with a performant way?
If you must remove entries while enumerating, walk the list in backward direction, and remove items that you need to remove.
for (var i = foo.Count-1 ; i >= 0 ; i--) {
if (MustBeRemoved(foo[i])) {
foo.RemoveAt(i);
}
}
Note that this is not required in case of your post, where you know the values that need to be removed.
foo.RemoveAll(x => x == 2);
In case you decide not to use for and foreach ;)
I assume your actual use case is more complicated than what you have laid out. So let's assume you actually have some condition at play that applies to each element and that multiple elements can satisfy. We'll call that a predicate.
List<T> exposes a RemoveAll method that allows you to supply a predicate. Any item that matches that predicate is then removed. For example
Func<int, bool> isEven = i => i % 2 == 0;
List<int> ints = ...
ints.RemoveAll(item => isEven(item));
// ints will only contain odd numbers
Other approaches to consider would be walking over the list backwards in a for loop and removing by index, building a second list containing the items to delete, and then in a second loop over the second list, remove items from the first. Or you could just write a query to construct a new sequence containing the items that you wish to keep.
Why do you need a loop?
foo.Remove(2);
I think the best way is to iterate backwards using a simple for loop.
for(int i = foo.Count-1; i>=0; i--)
if(foo[i]==2) foo.RemoveAt(i);
change your foreach as below
foreach (int i in new List<int>(foo))
If you want to remove arbitrary elements, and not just one, you can use RemoveAll and specify a predicate:
foo.RemoveAll(element => (element == 2));
You can add the items that you want to remove to a temporary list, then remove them after the loop:
List<int> foo = new List<int>();
foo.Add(1);
foo.Add(2);
foo.Add(3);
List<int> remove = new List<int>();
foreach (int i in foo) {
if (i==2) {
remove.Add(i);
}
}
foreach (int i in remove) {
foo.Remove(i);
}
You can't edit a list while you're iterating it.
Consider:
List<int> foo;
int[] bar = foo.ToArray();
foreach(int i in bar)
{
if (i == 2)
{
foo.Remove(i);
}
}
But beware: you should walk this list backwards, because removing an item from the foo list will mean the bar list no longer aligns with it. (If you don't walk backwards, you'll have to keep track of the count of removals and adjust the index passed to the remove call!)

C# method to remove duplicates from a List<T>

I need a C# method to remove duplicates from a List<T> using a custom comparison operation. In .NET 4. Is there one or do I have to write it myself?
Assuming your comparison operation is IEqualityComparer<T> or can be converted to it, you're fine with LINQ:
var newList = oldList.Distinct(customComparer).ToList();
Obviously that creates a new list rather than removing elements from the old one, but in most cases that's okay. You could always completely replace the contents of the old list with the new list afterwards if not...
You could go with Jon's answer, or if you really want to remove duplicates from an existing list, something like this would work:
public static void RemoveDuplicates<T>(this IList<T> list, IEqualityComparer<T> comparer = null)
{
comparer = comparer ?? EqualityComparer<T>.Default;
var uniques = new HashSet<T>(comparer);
for (int i = list.Count - 1; i >= 0; --i)
{
if (!uniques.Add(list[i]))
{
list.RemoveAt(i);
}
}
}

Efficient Linq Enumerable's 'Count() == 1' test

Similar to this question but rephrased for Linq:
You can use Enumerable<T>.Any() to test if the enumerable contains data. But what's the efficient way to test if the enumerable contains a single value (i.e. Enumerable<T>.Count() == 1) or greater than a single value (i.e. Enumerable<T>.Count() > 1) without using an expensive count operation?
int constrainedCount = yourSequence.Take(2).Count();
// if constrainedCount == 0 then the sequence is empty
// if constrainedCount == 1 then the sequence contains a single element
// if constrainedCount == 2 then the sequence has more than one element
One way is to write a new extension method
public static bool IsSingle<T>(this IEnumerable<T> enumerable) {
using (var enumerator = enumerable.GetEnumerator()) {
if (!enumerator.MoveNext()) {
return false;
}
return !enumerator.MoveNext();
}
}
This code take's LukeH's excellent answer and wraps it up as an IEnumerable extension so that your code can deal in terms of None, One and Many rather than 0, 1 and 2.
public enum Multiplicity
{
None,
One,
Many,
}
In a static class, e.g. EnumerableExtensions:
public static Multiplicity Multiplicity<TElement>(this IEnumerable<TElement> #this)
{
switch (#this.Take(2).Count())
{
case 0: return General.Multiplicity.None;
case 1: return General.Multiplicity.One;
case 2: return General.Multiplicity.Many;
default: throw new Exception("WTF‽");
}
}
Another way:
bool containsMoreThanOneElement = yourSequence.Skip(1).Any();
Or for exactly 1 element:
bool containsOneElement = yourSequence.Any() && !yourSequence.Skip(1).Any();
Efficient Count() == n test:
public static bool CountIsEqualTo<T>(this IEnumerable<T> enumerable, int c)
{
using (var enumerator = enumerable.GetEnumerator())
{
for(var i = 0; i < c ; i++)
if (!enumerator.MoveNext())
return false;
return !enumerator.MoveNext();
}
}
With linq to objects, SingleOrDefault throws if there is more than one element, so you're probably best off if you roll your own.
EDIT: Now I've seen LukeH's answer, and I have to say I prefer it. Wish I'd thought of it myself!
bool hasTwo = yourSequence.ElementAtOrDefault(1) != default(T);
...in case of class where values can be null this could maybe we useful.

Categories