In one of my projects I'm trying to remove an item from a list where the id is equal to the given id.
I have a BindingList<T> called UserList.
Lists have all the method RemoveAll().
Since I have a BindingList<T>, I use it like that:
UserList.ToList().RemoveAll(x => x.id == ID )
However, my list contains the same number of items as before.
Why it's not working?
It's not working because you are working on a copy of the list which you created by calling ToList().
BindingList<T> doesn't support RemoveAll(): it's a List<T> feature only, so:
IReadOnlyList<User> usersToRemove = UserList.Where(x => (x.id == ID)).
ToList();
foreach (User user in usersToRemove)
{
UserList.Remove(user);
}
We're calling ToList() here because otherwise we'll enumerate a collection while modifying it.
You could try:
UserList = UserList.Where(x => x.id == ID).ToList();
If you use RemoveAll() inside a generic class that you intend to be used to hold a collection of any type object, like this:
public class SomeClass<T>
{
internal List<T> InternalList;
public SomeClass() { InternalList = new List<T>(); }
public void RemoveAll(T theValue)
{
// this will work
InternalList.RemoveAll(x =< x.Equals(theValue));
// the usual form of Lambda Predicate
//for RemoveAll will not compile
// error: Cannot apply operator '==' to operands of Type 'T' and 'T'
// InternalList.RemoveAll(x => x == theValue);
}
}
This content is taken from here.
If there is just one item as unique ID inside bindinglist, the below simple code can work.
UserList.Remove(UserList.First(x=>x.id==ID));
Related
I have an observable collection I want to remove an element from. I have tried using the linq .Where syntax but i am getting stuck with the casting.
public class Atc
{
public string Code { get; set; }
public string Description { get; set; }
public string ParentDescriptions { get; set; }
}
i have defined the observable collection of medicines,
public ObservableCollection<Atc> MedicinesObservableCollection
and i have a method to filter out the item from the collection that works:
private void RemoveMedicineListItem(string code)
{
var filteredCollection = new ObservableCollection<Atc>();
foreach (var item in MedicinesObservableCollection)
{
if (item.Code != code)
{
filteredCollection.Add(item);
}
}
MedicinesObservableCollection = filteredCollection;
}
I have tried:
(ObservableCollection<Atc>)MedicinesObservableCollection.Where(x => x.Code != code);
but this gets a runtime exception
System.InvalidCastException: 'Unable to cast object of type 'WhereEnumerableIterator1[Atc]' to type 'System.Collections.ObjectModel.ObservableCollection1[Atc]'.'
i get this is something to do with the linq enumerables but i am out of my depth.
You can´t cast an instance of WhereIterator<Atc> (which your Where-clause actually returns) to ObservableCollection<Atc>). The returned instance only implements IEnumerable<Atc>, this is you can iterate it. However it doesn´t havy anything in common with any list, or in particular with an observable one.
However you can create a new one based on the returned value:
var result = new ObservableCollection(MedicinesObservableCollection.Where(x => x.Code != code));
All the answers are correct but when using ObservableCollection it would be better to do a remove on the same object rather then creating a new list.
MedicinesObservableCollection.Remove(MedicinesObservableCollection.FirstOrDefault(x => x.Code == code));
This is because recreating the collection might force all the items in the list to be re-rendered and this way only one item will be removed from the same collection.
Linq doesn't return a collection of the same type as the input. See this. So your cast to ObservableCollection<Atc> fails as it's some internal implementation of IEnumerable, not an ObservableCollection.
The solution is just to create a new ObservableCollection:
var items = new ObservableCollection<Atc>(MedicinesObservableCollection.Where(x => x.Code != code));
I have following TracefieldPartProgramClass
public class TracefieldPartProgramClass
{
public TracefieldPartProgramClass() { }
public ObservableCollection<Tuple<string, object, object>> obcTraceFieldPartProgram = new ObservableCollection<Tuple<string, object, object>>();
}
I use it to make the following collection:
ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>();
now after having filled it I want to be able to sort as I want (say on the Tracefield[0]).
So I implemented this:
private ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> SortOnTracefield(ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> obcToSort)
{
var obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>();
obcSorted = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString());<--- this is where I get the error
return obcSorted;
}
but when I do it, I get this error:
Error CS0266 Cannot implicitly convert type 'System.Linq.IOrderedEnumerable' to 'System.Collections.ObjectModel.ObservableCollection'. An explicit conversion exists (are you missing a cast?)
Try this one:
private ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> SortOnTracefield(ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> obcToSort)
{
var sorted = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString();
return new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(sorted);
}
You can pass an IEnumerable to the observable collection's constructor.
However, that returns a new instance of the collection. So, if the unsorted instance was bound to the GUI, that may not update your GUI. Another approch to support sorting is to use the ICollectionView.
The SO question how-do-i-sort-an-observable-collection may be intersting for you too. The following extension method can be used for sorting ObservableCollections without recreation:
static class Extensions
{
public static void Sort<T>(this ObservableCollection<T> collection) where T : IComparable
{
List<T> sorted = collection.OrderBy(x => x).ToList();
for (int i = 0; i < sorted.Count(); i++)
collection.Move(collection.IndexOf(sorted[i]), i);
}
}
However, your TracefieldPartProgramClass class needs to implement IComparable or you'll need to implement a custom IComparer<TracefieldPartProgramClass> and pass it the Sort method.
As the error says, you are trying to assign an IOrderedEnumerable to a variable of type ObservableCollection, which is not possible.
But luckily, ObservableCollection has a constructor that takes an IEnumerable and prefills it with the items of that IEnumerable.
So, in order to make that error disappear, use that:
var sortedEnumerable = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString());
obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(sortedEnumerable );
Enumerable.OrderBy doesn't return an ObservableCollection but an IEnumerable<T>, you can create a new with the List<T> constructor:
var obcSortedList = obcToSort
.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString())
.ToList();
var obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(obcSortedList);
I've written an extension method to add items to an (EF) EntityCollection. I got an interesting error, saying that my collection of IEnumerable ("items") had been modified, after the first loop in the foreach. When I turn items into items.ToList() (like in the code below), it works fine.
I completely understand that doing ToList() will produce a copy of the items on which the foreach will then operate.
What I do NOT understand is what is modifying the IEnumerable when I'm doing a foreach over it.
Update: Somehow, it seems the items variable is the same as the collections variable?
Update 2: I think collection and entity may be affected by EF's entity tracking, but I still fail to understand why
Usage:
ssp.ServiceAreas.ReplaceCollection(model.ServiceAreas);
Here's my extension method:
public static void AddOrUpdate<TEntity>(this EntityCollection<TEntity> collection, IEnumerable<TEntity> items)
where TEntity : EntityObject, IProjectIdentity<int>, new()
{
foreach (var item in items.ToList())
collection.AddOrUpdate(item);
}
public static void AddOrUpdate<TEntity>(this EntityCollection<TEntity> collection, TEntity item)
where TEntity : EntityObject, IProjectIdentity<int>, new()
{
if (item.ID > 0 && collection.Any(c => c.ID == item.ID))
collection.Remove(collection.First(c => c.ID == item.ID));
// For now, the Remove NEVER gets hit
collection.Add(item);
}
collection.Remove(collection.First(c => c.ID == item.ID));
you are removing in the collection you are iterating.
I created the following sample code:
internal class Program
{
private static void Main(string[] args)
{
var one = new List<string> {"Adam", "John"};
var two = new List<string> {"Adam", "Houldsworth"};
one.AddOrUpdate(two);
Console.Read();
}
}
static class Extensions
{
public static void AddOrUpdate(this IList<string> collection, IEnumerable<string> items)
{
foreach (var item in items.ToList())
collection.AddOrUpdate2(item);
}
public static void AddOrUpdate2(this IList<string> collection, string item)
{
if (collection.Any(c => c == item))
collection.Remove(collection.First(c => c == item));
collection.Add(item);
}
}
This works as you would expect, there are no errors. So in essence, none of the lines are causing issues.
What will cause issues is if you call the list on itself:
one.AddOrUpdate(one);
So from what I can see, you must be calling this extension method with the same collection as both arguments.
If you are, then both Remove or Add will mutate the collection and cause this exception.
Perhaps EntityCollection doesn't like when someone takes over it's elements? So, when you Add to collection, item gets Removed from items.
Or it could be that items == collection
It could be that the first item is always a new item, and thus by default the ID is set to some initial value. It will then be added to the collection, letting the EntityFramework generate a new ID and assign it to the first added item.
It then might be that the EntityCollection thinks it has changed, because it uses the ID to sort or do something else internally. And thus the foreach operation (which is probably using the same list) throws the exception. That's also why the test-case proved by Adam Houldsworth does not give the issue!
EntityCollection<Customer> customers = new EntityCollection<Customer>();
Customer newCustomer = new Customer() {ID = 0};
customers.Add(newCustomer);
customers.AddOrUpdate(customers);
sometimes, i just feel dumb...
i have a simple class:
public class myClass
{
public long Id { get; set; }
public long ParentChannelId { get; set; }
}
and i have a list that contains the class:
List<myClass> myItems = new List<myClass>
further down the code, i feed the list with classes.
now, i want to delete an item from the list.
but, since an item can have children and grandchilds etc...
i want to delete everything related..
was thinking of something like:
(pseudo code )
var List<myClass> itemsToDelete = myItems.Where(i => i.Ancestors.Contains(myItemId));
but i dont really have the brains atm to know how to write it exactly... :\
i do have the .Ancestors function...
just need help with the lambda linq
public List<Channel> Ancestors
{
get
{
List<MyCms.Content.Channels.Channel> result = new List<MyCms.Content.Channels.Channel>();
Channel channel = this;
while (channel != null)
{
result.Add(channel);
channel = myChannels.Where(c => c.ParentChannelId == this.Id).First();
}
result.Reverse();
return result;
}
}
EDIT: guess i did not explain myself as i should...
i have all the properties like ancestors, children parent etc...
i want to select all the classes that might contain the specific class...
I've re-read your question, especially the last part where you said you already have .Ancestors, and now it makes more sense.
Do this to get your list of items to delete:
List<MyClass> itemsToDelete = myItems
.Where(i => i.Id == myItemId)
.SelectMany(i => i.Ancestors)
.Concat(myItems) // Want to delete these too, not just the ancestors
.ToList()
;
Then you can foreach through the result, and remove them from the original list.
I'd suggest keeping these in a Dictionary<int, MyClass> or a HashSet<MyClass> instead of a list, since removal will be way faster.
For a HashSet, you'll have to implement Equals and GetHashCode, or create an IEqualityComparer<MyClass> implementation to provide those methods.
Before Edit:
I wouldn't write my code this way. I'd simply create a Dictionary<int, MyClass> instead of a list. It will do a lookup way faster than anything involving ancestors/tree traversal.
But here is how to accomplish what you're trying to accomplish:
If you're using Linq to Objects (as opposed to Linq to SQL or Linq to Entities), make a property called Parent on MyClass, of the correct type, instead of trying to link them by Id.
Then you can make an Ancestors property fairly easily:
public IEnumerable<MyClass> Ancestors
{
get
{
MyClass current = this;
while(current != null)
{
current = current.Parent;
yield return current;
}
}
}
If you can't edit the class, make an extension method called GetAncestors.
Then you can use something very similar to the code you wrote in your question:
List<MyClass> itemsToDelete = myItems
.Where(i => i.Ancestors.Any(a => a.Id == myItemId))
.ToList();
Linq to Entities
If you are using Linq to Entities, create a navigation property of the type MyClass to navigate to the parent, and do the same thing. Note that this might cause re-queries. Not sure the Linq can or would get translated into a hierarchical query.
This is how I would do it using a hashset and RemoveAll method.
var itemsToDelete = new HashSet<myClass>(otherItems);
myItems.RemoveAll(i => itemsToDelete.Contains(i));
RemoveAll Method
http://msdn.microsoft.com/en-us/library/wdka673a.aspx
I have two classes, CheckboxItemsList which extends a generic list, and CheckboxItems, which contains a list of objects of type CheckboxItem.
I want to use LINQ to be able to filter CheckboxItemsList based on properties of its CheckboxItems objects. The return type is always a generic list, though, but I want it to be a CheckboxItemsList.
So I guess the basic question is, can linq be made to return a list of the same type that it starts with? Since I can't cast a base class to a derived class, do I have any option other than iterating through the results of the linq query and rebuilding the derived list object row by row? Not that this is the end of the world, but I'm relatively new to linq and was wondering it there is a better way to do it.
What I want:
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed);
(obviously doesn't work since the query will return List<CheckboxItems>, not CheckboxItemsList)
The objects, generally:
public class CheckboxItemsList: List<CheckboxItems>
{
// does not add any fields, just access methods
}
public class CheckboxItems : IEnumerable<CheckboxItem>
{
public long PrimaryKey=0;
protected CheckboxItem[] InnerList;
public bool Changed
{
get {
return (InnerList.Any(item => item.Changed));
}
}
....
}
No, this is not possible out of the box. You'll need to add code to do this.
For example, you can add a constructor like so:
public CheckboxItemsList(IEnumerable<CheckboxItems> checkboxItems) {
// something happens
}
Then you can say
CheckboxItemsList newList = new CheckboxItemsList(
MyCheckboxItemsList.Where(item => item.Changed)
);
Additionally, you could add an extension method like so
static class IEnumerableCheckboxItemsExtensions {
public static ToCheckboxItemsList(
this IEnumerable<CheckboxItems> checkboxItems
) {
return new CheckboxItemsList(checkboxItems);
}
}
and then
CheckboxItemsList newList =
MyCheckboxItemsList.Where(item => item.Changed)
.ToCheckboxItemsList();
LINQ works on IEnumerable<T> and IQueryable<T> and the result types of all LINQ operations (Where, Select) etc, will return one of those. The standard ToList function returns a concrete list of type List<T>, you may need to come up with an extension method, e.g.:
public static CheckboxItemsList ToItemList(this IEnumerable<CheckboxItem> enumerable)
{
return new CheckboxItemsList(enumerable);
}
No, there's no built-in way to do this. You have two main options:
Add a constructor to your CheckboxItemsList class that takes an IEnumerable<CheckboxItems> or similar. Pass that collection on to the base List<T> constructor that takes an IEnumerable<T>. That base constructor should then populate the list for you:
var newList =
new CheckboxItemsList(MyCheckboxItemsList.Where(item=>item.Changed));
// ...
public class CheckboxItemsList : List<CheckboxItems>
{
public CheckboxItemsList(IEnumerable<CheckboxItems> collection)
: base(collection)
{
}
}
Create an extension method that takes an IEnumerable<CheckboxItems> or similar and returns a populated CheckboxItemsList:
var newList = MyCheckboxItemsList.Where(item=>item.Changed)
.ToCheckboxItemsList();
// ...
public static class EnumerableExtensions
{
public static CheckboxItemsList ToCheckboxItemsList(
this IEnumerable<CheckboxItems> source)
{
var list = new CheckboxItemsList();
foreach (T item in source)
{
list.Add(item);
}
return list;
}
}
(Of course, for completeness you could implement both of these options. The extension method would then just pass its IEnumerable<CheckboxItems> argument on to the constructor rather than manually looping and adding each item.)
You can also use "Conversion Operator", as below:
public class CheckboxItemsList: List<CheckboxItems>
{
public static implicit operator CheckboxItems(IEnumerable<CheckboxItems> items)
{
var list = new CheckboxItemsList();
foreach (var item in items)
{
list.Add(item);
}
return list;
}
}
Now, the below code would work.
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed);
From MSDN:
A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. This is described further in Section 6.1.
A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Explicit conversions can occur in cast expressions, and are described further in Section 6.2.
Here is what I came up with, building on the various suggestions of others. A generic extension method:
public static T ToList<T>(this IEnumerable baseList) where T : IList,new()
{
T newList = new T();
foreach (object obj in baseList)
{
newList.Add(obj);
}
return (newList);
}
So now I can do what I want:
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed)
.ToList<CheckboxItemsList>();
Another pretty obvious solution occurred to me, which is also useful for situations where the derived list class has field properties that I need to maintain in the new list.
Just create a new instance of my derived list class, and use AddRange to populate it.
// When created with a CheckboxItemsList parameter, it creates a new empty
// list but copies fields
CheckboxItemsList newList = new CheckboxItemsList(OriginalList);
newList.AddRange(OriginalList.Where(item => item.Changed));