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));
Related
I have the list like this
public class Col
{
public long Id { get; set; }
public Dictionary<string, dynamic> Dt { get; set; }
}
var list = IEnumerable<Col>;
I need to sort the List using dictionary
I tried a lot of methods, but nothing helps, there is an error:
System.ArgumentException: At least one object must implement IComparable.
The last thing I stopped at was:
list.OrderBy(x => x.Dt.Where(r => r.Key == "Smile").Select(r => r.Value));
Sorting should be done by the Dictionary value
Update1:
I want to sort the block that I have selected, but sort not by Id, but by the data in the dictionary
Try this:
list.OrderBy(c => c.Dt[<YourKeyValue>]);
How to Convert the below for each to Linq and if possible include the If statement into the Linq also.?
public class InvalidDataType
{
public string ViewName { get; set; }
public string ControlName { get; set; }
public string DataValue { get; set; }
}
foreach (InvalidDataType invalidField in DataObjectManager.GetInvalidFields())
{
if (invalidField.ControlName == "tbMCBNumber")
{
fieldToRemove = invalidField;
break;
}
}
if (fieldToRemove != null)
{
DataObjectManager.GetInvalidFields().Remove(fieldToRemove);
}
This is the code I tried
DataObjectManager.GetInvalidFields().Where(x => x.ControlName == "tbMCBNumber").Remove();
Where just returns a new collection, it doesn´t modifiy the existing one. That´s what the Q in LINQ stands for: query. This means linq isnt designed and therefor not really good when modifying collections. This is whyt you shouldn´t use LINQ when modifying the elements within your collections.
If your GetInvalidFields-methods returns a List<T> you can do the following:
DataObjectManager.GetInvalidFields().RemoveAll(x => x.ControlName == "tbMCBNumber");
This however isn´t LINQ, it´s just a method for ICollection<T>, which List<T> implements.
Alternativly you could use a single for-loop that couints backwards in order to remove some items from it:
var list = DataObjectManager.GetInvalidFields();
for(int i = list.Count - 1; i >= 0; i--)
{
if(list[i].ControlName == "tbMCBNumber").Remove(list[i]);
}
However this assumes you even can modify the underlying collection that GetInvalidFields returns. If that method returns something that can´t be modified at all like a ReadOnlyCollection or just en iterator there´s no way to remove items from that.
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 have a linq query as follow:
public static ViewUserDisplayPreferences GetUserDisplayPreferences(int TheUserID)
{
using ( MyDataContext TheDC = new MyDataContext() )
{
var OutputUserDisplayPreferences = from user in TheDC.Users
where user.UserID == TheUserID
select new ViewUserDisplayPreferences
{
UserTimeFormat = user.UserTimeDisplay
};
return (ViewUserDisplayPreferences)(OutputUserDisplayPreferences);
}
}
For the moment, the object ViewUserDisplayPreferences is defined as follow (more variables will be added later):
public class ViewUserDisplayPreferences
{
public string UserTimeFormat { get; set; }
};
On the return statement at runtime, I get this error:
Unable to cast object of type
'System.Data.Linq.DataQuery`1[ObjectsUsers.ViewUserDisplayPreferences]'
to type
'ObjectsUsers.ViewUserDisplayPreferences'.]
What's wrong with the code? The intellisense is not showing any error on the line?
Thanks
OutputUserDisplayPreferences is an IEnumerable<T>. If you want an object, use either the First (if there can be more than one) or Single (if you know for sure only one object will be in the sequence) method on the sequence. If it is possible for the sequence to be empty, use the respective *OrDefault method.
Linq returns a collection. Try adding a .FirstOrDefault to the end.
I just can't figure out why the item in my filtered list is not found. I have simplified the example to show it. I have a class Item...
public class Item
{
public Item(string name)
{
Name = name;
}
public string Name
{
get; set;
}
public override string ToString()
{
return Name;
}
}
... and a class 'Items' which should filter the items and check if the first item is still in the list...
public class Items
{
private IEnumerable<Item> _items;
public Items(IEnumerable<Item> items)
{
_items = items;
}
public List<Item> Filter(string word)
{
var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
Console.WriteLine("found: " + ret.Contains(_items.First()));
// found: false
return ret;
}
}
The executing code looks like this:
static void Main(string[] args)
{
string[] itemNames = new string[] { "a", "b", "c" };
Items list = new Items(itemNames.Select(x => new Item(x)));
list.Filter("a");
Console.ReadLine();
}
Now, if I execute the program, the Console.WriteLine outputs that the item is not found. But why?
If I change the first line in the constructor to
_items = items.ToList()
then, it can find it. If I undo that line and call ToList() later in the Filter-method, it also cannot find the item?!
public class Items
{
private IEnumerable<Item> _items;
public Items(IEnumerable<Item> items)
{
_items = items;
}
public List<Item> FilteredItems
{
get; set;
}
public List<Item> Filter(string word)
{
var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
_items = _items.ToList();
Console.WriteLine("found: " + ret.Contains(_items.First()));
// found: false
return ret;
}
}
Why is there a difference where and when the lambda expression is executed and why isn't the item found any more? I don't get it!
The reason is deferred execution.
You intialize the _items field to
itemNames.Select(x => new Item(x));
This is a query, not the answer to that query. This query is executed every time you iterate over _items.
So in this line of your Filter method:
var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
the source array is enumerated and a new Item(x) created for each string. These items are stored in your list ret.
When you call Contains(_items.First()) after that, First() again executes the query in _items, creating new Item instances for each source string.
Since Item's Equals method is probably not overridden and performs a simple reference equality check, the first Item returned from the second iteration is a different instance of Item than the one in your list.
Let's remove extra code to see the problem:
var itemNames = new [] { "a", "b", "c" };
var items1 = itemNames.Select(x => new Item(x));
var surprise = items1.Contains(items1.First()); // False
The collection items1 appears not to contain its initial element! (demo)
Adding ToList() fixes the problem:
var items2 = itemNames.Select(x => new Item(x)).ToList();
var noSurprise = items2.Contains(items2.First()); // True
The reason why you see different results with and without ToList() is that (1) items1 is evaluated lazily, and (2) your Item class does not implement Equals/GetHashCode. Using ToList() makes default equality work; implementing custom equality check would fix the problem for multiple enumeration.
The main lesson from this exercise is that storing IEnumerable<T> that is passed to your constructor is dangerous. This is only one of the reasons; other reasons include multiple enumeration and possible modification of the sequence after your code has validated its input. You should call ToList or ToArray on sequences passed into constructors to avoid these problems:
public Items(IEnumerable<Item> items) {
_items = items.ToList();
}
There are two problems in your code.
First problem is that you are initializing a new item every time. That is you don't store the actual items here when you write.
IEnumerable<Item> items = itemNames.Select(x => new Item(x));
The execution of Select is deferred. i.e every time you call .ToList() a new set of Items is created using itemNames as source.
Second problem is that you are comparing items by reference here.
Console.WriteLine("found: " + ret.Contains(_items.First()));
When you use ToList you store items in list and the references remains same so you will find item with reference.
When you don't use ToList the references are not same any more. because everytime a new Item is created. you cant find your item with different reference.