RemoveAll for ObservableCollections? - c#

I am looking for Linq way (like RemoveAll method for List) which can remove selected items from my ObservableCollection.
I am too new to create an extension method for myself. Is there any way I remove items from ObservableCollection passing a Lambda expression?

I am not aware of a way to remove only the selected items. But creating an extension method is straight forward:
public static class ExtensionMethods
{
public static int Remove<T>(
this ObservableCollection<T> coll, Func<T, bool> condition)
{
var itemsToRemove = coll.Where(condition).ToList();
foreach (var itemToRemove in itemsToRemove)
{
coll.Remove(itemToRemove);
}
return itemsToRemove.Count;
}
}
This removes all items from the ObservableCollection that match the condition. You can call it like that:
var c = new ObservableCollection<SelectableItem>();
c.Remove(x => x.IsSelected);

Iterating backwards should be more efficient than creating a temporary collection as in Daniel Hilgarth's example.
public static class ObservableCollectionExtensions
{
public static void RemoveAll<T>(this ObservableCollection<T> collection,
Func<T, bool> condition)
{
for (int i = collection.Count - 1; i >= 0; i--)
{
if (condition(collection[i]))
{
collection.RemoveAt(i);
}
}
}
}

How about this implementation for a one-liner?
observableCollection.Where(l => l.type == invalid).ToList().All(i => observableCollection.Remove(i))
-- Edit --
Sorry, yes, you need a ToList() in the middle to force the first half to evaluate, as LINQ does lazy evaluation by default.

Each of solution proposed here which uses routine to remove item one by one has one fault. Imagine that you have many items in observable collection, lets say 10.000 items. Then you want to remove items which meets some condition.
If you use solution from Daniel Hilgarth and call: c.Remove(x => x.IsSelected); and there are for example 3000 items to be removed, proposed solution will notify about each item removal. This is due to fact that internal implementation of Remove(item) notify about that change. And this will be called for each of 3000 items in removal process.
So instead of this i created descendant of ObservableCollection and add new method RemoveAll(predicate)
[Serializable]
public class ObservableCollectionExt<T> : ObservableCollection<T>
{
public void RemoveAll(Predicate<T> predicate)
{
CheckReentrancy();
List<T> itemsToRemove = Items.Where(x => predicate(x)).ToList();
itemsToRemove.ForEach(item => Items.Remove(item));
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
Interesting line is itemsToRemove.ForEach(item => Items.Remove(item));. Calling directly Items.Remove(item) will not notify about item removed.
Instead after removal of required items, changes are notified at once by calls:
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

This is my version of an extension method solution, which is only a slight variation on the accepted answer, but has the advantage that the count returned is based on confirmed removal of the item from the collection:
public static class ObservableCollectionExtensionMethods
{
/// <summary>
/// Extends ObservableCollection adding a RemoveAll method to remove elements based on a boolean condition function
/// </summary>
/// <typeparam name="T">The type contained by the collection</typeparam>
/// <param name="observableCollection">The ObservableCollection</param>
/// <param name="condition">A function that evaluates to true for elements that should be removed</param>
/// <returns>The number of elements removed</returns>
public static int RemoveAll<T>(this ObservableCollection<T> observableCollection, Func<T, bool> condition)
{
// Find all elements satisfying the condition, i.e. that will be removed
var toRemove = observableCollection
.Where(condition)
.ToList();
// Remove the elements from the original collection, using the Count method to iterate through the list,
// incrementing the count whenever there's a successful removal
return toRemove.Count(observableCollection.Remove);
}
}

There is no way to pass an expression to the ObservableCollection to remove matching items, in the same way that a generic list has. ObservableCollection adds and removes one item at a time.
You will have to create your own implementation of INotifyCollectionChanged in order to do this, or as you mention create an extension method.

ObservableCollection<AppVariable<G>> _appVariables = new new ObservableCollection<AppVariable<G>>();
var temp = AppRepository.AppVariables.Where(i => i.IsChecked == true).OrderByDescending(k=>k.Index);
foreach (var i in temp)
{
AppRepository.AppVariables.RemoveAt(i.Index);
}

Kinda late but just posting this up here since I couldn't find another solution online while I ran into this same issue:
obj objToRemove = Collection.First(obj => obj.ID == ID);
Collection.Remove(objToRemove);
Assuming u have an ID or Name of sorts where u can get your desired object to remove, you can use the '.First' method from ObservableCollections to get your object to remove and call the '.Remove' method to remove the selected item.
Alternatively, you can just throw the entire first line into the Remove method.
Collection.Remove(Collection.First(obj => obj.ID == ID));

Related

Does a non-concurrent bag exist

Is there such a thing as a non-concurrent bag? I see lots of mentions of ConcurrentBag, but nothing about Bag.
Does such a collection exist?
To clarify why I'd like to use such a collection, I often find that the order of a collection becomes an important but potentially difficult to trace property of a collection.
I'm not necessarily saying that this would often happen in good, well designed code, but there are situations where I wish I could say "do not expect any order on this collection, order it specifically as and when you need to".
No, but since it documented as being "optimized for scenarios where the same thread will be both producing and consuming data stored in the bag", you can just use ConcurrentBag in both concurrent and non-concurrent scenarios.
You could always write your own class that "hides" the order of the items.
For example:
public sealed class NonConcurrentBag<T>: IReadOnlyCollection<T>
{
public void Add(T item)
{
// When adding an item, add it to a random location to avoid callers assuming an ordering.
if (_items.Count == 0)
{
_items.Add(item);
return;
}
int index = _rng.Next(0, _items.Count);
_items.Add(_items[index]);
_items[index] = item;
}
public void Clear()
{
_items.Clear();
}
public T Take()
{
if (_items.Count == 0)
throw new InvalidOperationException("Attempting to Take() from an empty NonConcurrentBag");
var result = _items[_items.Count - 1];
_items.RemoveAt(_items.Count - 1);
return result;
}
public bool IsEmpty => _items.Count == 0;
public IEnumerator<T> GetEnumerator()
{
return _items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count => _items.Count;
readonly List<T> _items = new List<T>();
readonly Random _rng = new Random();
}
When you add an item, this sneakily adds it at a random index (shifting the displaced item to the end of the list).
That means that not only is the index of an item random when you add it, it can also move to a different index as you add more items. That'll foil anything that expects any particular order!
Adding and taking items is an O(1) operation except when adding and the underlying list needs to be resized, when it is an O(N) operation.

Is it possible to implement RemoveAll() on EntityCollection<T>?

I have a question similar to this one, but pertaining to EntityCollection<>.
EntityCollection implements Remove(), allowing you to remove a single item from the list at once. However, I'd like to implement an extension method that can remove multiple items at once, similar to IList<T>'s RemoveAll(Predicate<T> match) method.
One idea would be to loop through the list, and remove items. Something like:
public static void RemoveAll<T>(this EntityCollection<T> collection, Predicate<T> match) where T : EntityObject
{
foreach (T o in collection)
{
if (match(o))
collection.Remove(o);
}
}
However, this will throw an exception because you can't modify the collection you're iterating through.
Another idea would be to build a temporary list of items to remove, then loop through that list and remove each item from the collection. However, this seems inefficient to me. Is there a better implementation?
As I said in comments, iterating over a secondary list is probably the only safe choice here.
You can implement it with something like:
public static void RemoveAll<T>(this EntityCollection<T> collection,
Predicate<T> match) where T : EntityObject
{
if (match == null) {
throw new ArgumentNullException("match");
}
collection.Where(entity => match(entity))
.ToList().ForEach(entity => collection.Remove(entity));
}

Is there an AddUnique method similar to Addrange() for alist in C#

I have a list in C#:
var list = new List<Car>();
list.AddRange(GetGreenCars());
list.AddRange(GetBigCars());
list.AddRange(GetSmallCars());
the issue is that some of the same cars get returned in different functions and I don't want them in the list more than once. Each car has a unique Name attribute. Is there anyway I can have something like this above but will only add items if they are unique ?
One choice is to add them and remove the repeated ones:
var list = new List<Car>();
list.AddRange(GetGreenCars());
list.AddRange(GetBigCars());
list.AddRange(GetSmallCars());
list = list.Distinct().ToList();
Another option is to do something like:
public static void AddUnique<T>( this IList<T> self, IEnumerable<T> items )
{
foreach(var item in items)
if(!self.Contains(item))
self.Add(item);
}
var list = new List<Car>();
list.AddUnique(GetGreenCars());
list.AddUnique(GetBigCars());
list.AddUnique(GetSmallCars());
A List<T> doesn't seem to be the appropriate collection here. You probably want an ISet<T> implementation such as HashSet<T> (or SortedSet<T> if you need ordering).
To allow this, you will need to write an IEqualityComparer<T> implementation that defines equality between cars according to the Name property. If this is the 'canonical' definition of car-equality, you can also consider directly building this definition into the Car type itself (object.Equals, object.GetHashCode and ideally implement IEquatable<T> too).
Given you override the .Equals() method for Car to determine one car object is the same as another car object, then the following should work w/o writing an extension method.
var list = new List<Car>();
list.AddRange(GetGreenCars()?.Except(list) ?? new List<Car>());
list.AddRange(GetBigCars()?.Except(list) ?? new List<Car>());
list.AddRange(GetSmallCars()?.Except(list) ?? new List<Car>());
Yet another option using Linq:
public static void AddUnique<T>(this IList<T> self, IEnumerable<T> items)
{
self.AddRange(
items.Where(x => self.FirstOrDefault(y => y.Name == x.Name) ==
null).ToList());
}
var list = new List<Car>();
list.AddUnique(GetGreenCars());
list.AddUnique(GetBigCars());
list.AddUnique(GetSmallCars());
I created an extension method that adds only unique values to anything implementing ICollection<T> (including a List<T>) from an IEnumerable<T>. Unlike implementations that use List<T>.Contains(), this method allows you to specify a lambda expression that determines if two items are the same.
/// <summary>
/// Adds only items that do not exist in source. May be very slow for large collections and some types of source.
/// </summary>
/// <typeparam name="T">Type in the collection.</typeparam>
/// <param name="source">Source collection</param>
/// <param name="predicate">Predicate to determine whether a new item is already in source.</param>
/// <param name="items">New items.</param>
public static void AddUniqueBy<T>(this ICollection<T> source, Func<T, T, bool> predicate, IEnumerable<T> items)
{
foreach (T item in items)
{
bool existsInSource = source.Where(s => predicate(s, item)).Any();
if (!existsInSource) source.Add(item);
}
}
Usage:
source.AddUniqueBy<Foo>((s, i) => s.Id == i.Id, items);
and if you want to compare one property (id in this case), this should work
var list = new List<string>();
list.AddRange(GetGreenCars().Where(greencar => !list.Contains(greencar, car => car.id == greencar.id)));
list.AddRange(GetBigCars().Where(bigcar => !list.Contains(bigcar, car => car.id == bigcar.id)));
list.AddRange(GetSmallCars().Where(smallcar => !list.Contains(smallcar, car => car.id == smallcar.id)));
Assuming your Get*Cars() return Lists of Car, another option could be:
var list = new List<Car>();
GetGreenCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
GetBigCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
GetSmallCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
Linq-less option!
A different option in case the programmer cannot, or don't wanna use Linq.
var list = new List<Car>();
list.AddRange(GetGreenCars().FindAll((x) => !list.Contains(x)));
list.AddRange(GetBigCars().FindAll((x) => !list.Contains(x)));
list.AddRange(GetSmallCars().FindAll((x) => !list.Contains(x)));
If the initial list is empty as in the example above, you can actually avoid using FindAll(...) on the first AddRange().

Convert List<T> to ObservableCollection<T> in WP7

I don't know if it's just too late or what, but I don't see how to do this...
What I'm expecting to do, and what the object browser says is there, is this:
var oc = new ObservableCollection<T>( new List<T>() );
But ObservableCollection<T> has a single parameterless constructor. The object browser says there is 2 overloads where List and IEnuerable should be able to be passed in.
Is there something wrong with my setup? Are the constructors not on the phone version? (that would be strange)
If this really doesn't exist, what is the standard way of doing this with WP7 now?
ObservableCollection has several constructors which have input parameter of List<T> or IEnumerable<T>:
List<T> list = new List<T>();
ObservableCollection<T> collection = new ObservableCollection<T>(list);
Apparently, your project is targeting Windows Phone 7.0. Unfortunately the constructors that accept IEnumerable<T> or List<T> are not available in WP 7.0, only the parameterless constructor. The other constructors are available in Silverlight 4 and above and WP 7.1 and above, just not in WP 7.0.
I guess your only option is to take your list and add the items into a new instance of an ObservableCollection individually as there are no readily available methods to add them in bulk. Though that's not to stop you from putting this into an extension or static method yourself.
var list = new List<SomeType> { /* ... */ };
var oc = new ObservableCollection<SomeType>();
foreach (var item in list)
oc.Add(item);
But don't do this if you don't have to, if you're targeting framework that provides the overloads, then use them.
To convert List<T> list to observable collection you may use following code:
var oc = new ObservableCollection<T>();
list.ForEach(x => oc.Add(x));
You'll have to write your own extension method to do this:
public static class CollectionEx
{
/// <summary>
/// Copies the contents of an IEnumerable list to an ObservableCollection
/// </summary>
/// <typeparam name="T">The type of objects in the source list</typeparam>
/// <param name="enumerableList">The source list to be converted</param>
/// <returns>An ObservableCollection containing the objects from the source list</returns>
public static ObservableCollection<T> ToObservableCollection<T>( this IEnumerable<T> enumerableList )
{
if( enumerableList != null ) {
// Create an emtpy observable collection object
var observableCollection = new ObservableCollection<T>();
// Loop through all the records and add to observable collection object
foreach( var item in enumerableList ) {
observableCollection.Add( item );
}
// Return the populated observable collection
return observableCollection;
}
return null;
}
}
Extension method from this answer IList<T> to ObservableCollection<T> works pretty well
public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> enumerable) {
var col = new ObservableCollection<T>();
foreach ( var cur in enumerable ) {
col.Add(cur);
}
return col;
}
ObservableCollection<FacebookUser_WallFeed> result = new ObservableCollection<FacebookUser_WallFeed>(FacebookHelper.facebookWallFeeds);
Use this:
List<Class1> myList;
ObservableCollection<Class1> myOC = new ObservableCollection<Class1>(myList);
If you are going to be adding lots of items, consider deriving your own class from ObservableCollection and adding items to the protected Items member - this won't raise events in observers. When you are done you can raise the appropriate events:
public class BulkUpdateObservableCollection<T> : ObservableCollection<T>
{
public void AddRange(IEnumerable<T> collection)
{
foreach (var i in collection) Items.Add(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
}
}
When adding many items to an ObservableCollection that is already bound to a UI element (such as LongListSelector) this can make a massive performance difference.
Prior to adding the items, you could also ensure you have enough space, so that the list isn't continually being expanded by implementing this method in the BulkObservableCollection class and calling it prior to calling AddRange:
public void IncreaseCapacity(int increment)
{
var itemsList = (List<T>)Items;
var total = itemsList.Count + increment;
if (itemsList.Capacity < total)
{
itemsList.Capacity = total;
}
}
I made an extension so now I can just load a collection with a list by doing:
MyObservableCollection.Load(MyList);
The extension is:
public static class ObservableCollectionExtension
{
public static ObservableCollection<T> Load<T>(this ObservableCollection<T> Collection, List<T> Source)
{
Collection.Clear();
Source.ForEach(x => Collection.Add(x));
return Collection;
}
}
The answer provided by Zin Min solved my problem with a single line of code. Excellent!
I was having the same issue of converting a generic List to a generic ObservableCollection to use the values from my List to populate a ComboBox that is participating in binding via a factory class for a WPF Window.
_expediteStatuses = new ObservableCollection<ExpediteStatus>(_db.getExpediteStatuses());
Here is the signature for the getExpediteStatuses method:
public List<ExpediteStatus> getExpediteStatuses()

simulate List like hashSet in C# so that List will have noduplicate values

I want to add colections to List, but only if Advertisements does not already exist in it. I know that HashSet works like this that has no duplicate values, but with HashSet i can not use AddRange and GetRange.
So is it possible to simulate List like hashSet?
List<Advertisements> advertisements = new List<Advertisements>();
advertisements.AddRange(NHibernateSession.CreateCriteria<Advertisements>()
.CreateCriteria(AdvertisementsProperties.City.ToString())
.Add(Restrictions.Or(
Restrictions.Like(CitiesProperties.Name.ToString(), text, MatchMode.Anywhere),
Restrictions.Like(CitiesProperties.SlovenianName.ToString(), text, MatchMode.Anywhere)))
.List<Advertisements>());
advertisements.AddRange(NHibernateSession.CreateCriteria<Advertisements>()
.CreateCriteria(AdvertisementsProperties.Area.ToString())
.Add(Restrictions.Or(
Restrictions.Like(AreasProperties.Name.ToString(), text, MatchMode.Anywhere),
Restrictions.Like(AreasProperties.SlovenianName.ToString(), text, MatchMode.Anywhere)))
.List<Advertisements>());
To add a bunch of items to a HashSet like AddRange does simply use:
set.UnionWith(items);
The items in a HashSet are not indexed (it's implemented as a hash table which is not designed for index based access to elements). If you strictly need to store items by index, you'll have to use a simple list and create your own Add method that checks Contains on each element before adding it to the underlying list. Of course, a linear list doesn't provide the efficiency of set operations as HashSet does.
Check out this post. The poster used an extension method to add "AddRange" functionality to HashSet.
You'd probably want to create your own class if you often need a hybrid List/Set collection functionality. It would go something like this:
using System;
using System.Collections;
using System.Collections.Generic;
public class ListSet<T> : IList<T>, ISet<T> // ISet<T> is supported only from .NET 4.0 on
{
#region Inner collections
private HashSet<T> _innerSet = new HashSet<T>();
private List<T> _innerList = new List<T>();
#endregion
#region The read methods delegate to the inner collection which is more appropriate and efficient:
public bool Contains(T item)
{
return this._innerSet.Contains(item);
}
public int IndexOf(T item)
{
return this._innerList.IndexOf(item);
}
// TODO: Implement all other read operations
#endregion
#region The write methods must keep both inner collections synchronized
public bool Add(T item)
{
bool wasAdded = this._innerSet.Add(item);
if (wasAdded) this._innerList.Add(item);
return wasAdded;
}
public void AddRange(IEnumerable<T> items)
{
foreach (T item in items) this.Add(item);
}
public bool Remove(T item)
{
if (this._innerSet.Remove(item))
{
return this._innerList.Remove(item);
}
return false;
}
// TODO: Implement all other write operations
// TODO: Consider implementing roll back mechanisms in exception handlers
// when write operations fail
#endregion
}

Categories