Comparing Items in two ObservableCollections - c#

I have two ObservableCollections, one that contains a current list of employees and one that contains a new one. If the new one contains an employee that is not in the old list, I want to add it to the old list.
If the new list does not contain an employee in the old list, I want to remove it from the old list. (I hope that makes sense).
Here is what I have tried;
foreach (var employee in _currentUsers.ToList())
{
if (!newUsers.Contains(employee))
{
Dispatcher.Invoke(() =>
{
_currentUsers.Remove(employee);
CurrentUsageDataGrid.Items.Refresh();
});
}
}
foreach (var employee in newUsers)
{
if (!_currentUsers.Contains(employee))
{
Dispatcher.Invoke(() =>
{
_currentUsers.Add(employee);
CurrentUsageDataGrid.Items.Refresh();
});
}
}
This is not working as even when I know that the lists haven't changed the Dispatcher.Invoke() is still firing. Am I misunderstanding how Contains operates and/or is there a 'better' way of performing a check like this?

Does your employee class implement IEquatable<T> interface, the method Contains of ObservableCollection will use Equal in that interface to compare object.
Otherwise you may need to use Linq to check for existing employee instead of using Contains
More reference on MSDN, https://msdn.microsoft.com/en-us/library/ms132407(v=vs.100).aspx

As was mentioned above you need to implement/override an Equal method for the comparison. When you do a "Contains" check on 2 class objects, it checks if the 2 objects are equal (not if their properties are). You can do the equal using an id, or you could compute a hashvalue and compare those.

Related

Ordered collection based on DateTime

I'm looking for a way to implement a collection which guarantees order based on a DateTime value. My initial idea was to use a SortedSet<T1> with a custom IComparer<T1> like this:
internal class DateTimeComparer : IComparer<MyType> {
public int Compare(MyType x, MyType y) {
return x.DateTimeProp.CompareTo(y.DateTimeProp);
}
}
var sortedSet = new SortedSet<MyType>(new DateTimeComparer());
However this doesn't work as the set seems to override/replace all items which have the exact timestamp. To validate this assumption, I created two collections, one being a simple list which was sorted using the DateTime property after its was populated, and another one based on the SortedSet<T1> -> The one based on the set had several entries missing which happened to be the ones which had the exact same timestamp.
What other options are there to implement such a collection?
An efficient way to maintain a sorted collection of items is to use a binary search tree. You could of course build your own binary search tree, however the SortedSet<T> class is implemented using a red-black binary search tree so it seems smarter to reuse that class which is exactly what you are trying to do.
The ordering of items in SortedSet<T> is controlled by comparing pairs of items by calling the IComparer<T>.Compare method. If this method returns 0 the two items are considered equal and only one of these items will be stored in the set. In your case with DateTimeComparer you get the problem that only a single MyType instance with a specifiec DateTimeProp can be stored in the set.
To solve this problem you have to make sure that distinct MyType instances never are equal when compared using the DateTimeComparer.Compare method. You can modify your code to achieve this:
class DateTimeComparer : IComparer<MyType> {
readonly ObjectIDGenerator idGenerator = new ObjectIDGenerator();
public int Compare(MyType x, MyType y) {
if (x.DateTimeProp != y.DateTimeProp)
return x.DateTimeProp.CompareTo(y.DateTimeProp);
bool firstTime;
var xId = idGenerator.GetId(x, out firstTime);
var yId = idGenerator.GetId(y, out firstTime);
return xId.CompareTo(yId);
}
}
If the two instances have different values of DateTimeProp then they should be ordered according to these. This is handled by the initial if statement.
If the two values have the same DateTimeProp values they need to be ordered based on some other criteria. You can use other properties of MyType but there might be cases where these properties are equal and it is important that the method never returns 0 except if x and y refers to the same instances (e.g. ReferenceEquals(x, y) is true).
To handle this you can use an ObjectIDGenerator which will assign unique 64 bit ID values to distinct instances. These can then be compared to provide an ordering.
Note that the ordering of items with same DateTimeProp values will be random but consistent. To control this ordering you can use other properties of MyType but eventually you will have to use the generated ID to provide an ordering when all properties of two different instances are the same.

list Remove method does not remove list item

I have an ASP.Net web app written in C# and it has a List<Employee> object called AffectedEmployees, where Employee is a class I've written. AffectedEmployees is also a property of another class I've written called LitHoldDetails, but I don't think that's pertinent; I just include the information in case I'm wrong, which has, of course, never happened. ;) Anyway, I need to remove a particular Employee object from AffectedEmployees. I first just did a Remove() (Add() works, BTW), but the Employee in question was not removed from AffectedEmployees. So I checked to make sure it was really there by doing a call to Contains(). Contains() doesn't find the Employee in AffectedEmployees, but if I examine the list and compare it to the Employee in the debugger, the Employee is indeed in Affected Employees. I've compared the two with a fine tooth comb and I see no difference in the data. What am I doing wrong? Here's my code:
Employee emp = new Employee().FindByLogin(item.Key.ToString());
if (CurrentLitHoldDetails.AffectedEmployees.Contains(emp))
CurrentLitHoldDetails.AffectedEmployees.Remove(emp);
Note: FindByLogin() initiates emp and fills it with data. item.Key.ToString() is coming from a Hashtable that contains all the Employees I need to remove
You're creating a new Employee object that couldn't possibly exist in the list yet, then trying to remove it. So it doesn't find that exact object (even if the contents of the new Employee object match some other Employee object in the list) and doesn't remove it. If you place a breakpoint on the line with Remove on it, you'll see it doesn't get hit.
Try using RemoveAll to remove all elements matching some condition. (I'm doing a bit of guessing here since I don't know what your classes look like exactly.)
CurrentLitHoldDetails.AffectedEmployees.RemoveAll(x => x.Login == item.Key.ToString());
Remove and Contains test for the presence of the exact object you're trying to remove, not just one with identical data. So when you create a new Employee object, it's looking for that exact object. It doesn't find it, because that exact object has never been added to AffectedEmployees.
This is called "reference equality" and is the default behavior. If you want Remove and Contains to determine equality based on property values, you will need to override the Equals and GetHashCode methods on your Employee object. (See here for Microsoft's documentation on Object.GetHashCode().)

Removing items found in another list

I have two lists and they are named: currentItems and newItems. newItems contains items found in currentItems and I am trying to those items before I output the list.
I have done some searching and I have tried using:
var newList = newItems.Except(CurrentItems).ToList();
but when I look at the output I still find the items from currentItems in the list. I found this example when I came across this question:
Quickest way to compare two List<>
which is similar to what I am trying to achieve but the all answers to the question do not work for me.
I have tried using:
List<ListItem> newList = newItems.Union(CurrentItems).ToList();
but I believe I am using it in the wrong situation since I am trying to remove the item completely from the list.
I have also tried looping through both loops, but I don't believe that is as efficient as it can be.
In the first example is there something I may be doing wrong with it? Or is there a different way to achieve my goal?
IEnumerable.Except will do what you want but it uses the default equality comparer. For custom objects you will need to implement Equals and GetHashCode
Also note that if newItems has duplicate values IEnumerable.Except will also do a distinct on your list.
EDIT2: You need an equality comparer that compares ListItem's I believe.
You'll need to pass in a custom comparor that compares just the Value property of the ListItem.
var newList = newItems.Except(currentItems, new ListItemValueComparer());
And the custom equality comparer is here...
class ListItemValueComparer : IEqualityComparer<ListItem>
{
public bool Equals(ListItem x, ListItem y)
{
return x.Value.Equals(y.Value);
}
public int GetHashCode(ListItem obj)
{
return obj.Value.GetHashCode();
}
}

Listbox contains does not find item, cannot remove an item

I add items to a list box like so:
foreach(myObject object in ListOfObjects)
{
mylistbox1.add(object);
}
foreach(myObject object in ListOfObjectsTwo)
{
mylistbox2.add(object);
}
Further on, I want to remove a couple of items given a specific condition. This is what I do:
foreach(myObject object in ListOfObjects3)
{
mylistbox1.items.remove(object);
mylistbox2.items.remove(object);
}
This only seems to work for mylistbox1 but not mylistbox2. When I debug, I can see that the item is there and that it has the exact same properties as the one I'm trying to remove. When I try to check if the listbox contains the item im trying to remove, it returns false.
I can't seem to make sense of it.
I c# all lists operation that are in some way required to compare objects use Equals or GetHashCode methods. In your case Equals and the default implementation wont check properties values it will only verify if the passed as argument object is in list so consider if you have the same instance in your list or just two different instances that happened to have the same properties. (the helpful VS option is make object id it will mark instance with a number)
If this is the case then you should consider overriding Equals method or find the instance that you want to delete with linq for example and pas that object to Remove method.

Inheritance and caching in ASP.NET

I've got some class hierarchy - base class which is named "Group", which contains base information about single group, and class named "RootGroup" which inherits from "Group" and extend base class with some properties. List of groups is stored in the cache using base class: e.g. IEnumerable (some of them are ordinary groups, and some of them are root groups. The point is when the collection is being retrieved from the cache and cast back to IEnumerable type, specific information of RootGrop items are lost. Is there any way to prevent this situation except of remembering type of each cached item?
Jimmy
You can check each item as you retrieve it, and cast it to its proper type:
foreach (object item in list) {
if (item is RootGroup) {
Rootgroup rootGroup = item as RootGroup;
// do stuff with rootGroup
}
}
If you are using a generic collection, like a List<Group>, you can change the foreach like this:
foreach (Group item in list)...
If I understand your question correctly, your properties of RootGroup are not being "lost". The issue is you have a Group view of a RootGroup object. In order to access RootGroup properties, you must cast the object to a RootGroup.
A simple check:
if(groupItem is RootGroup)
{
RootGroup rootGroupItem = groupItem as RootGroup;
// Do stuff
}
Another way...
//from cache
IEnumerable<Group> groupsFromCache = GetGroupsFromCache();
IEnumerable<RootGroup> rootGroups = groupsFromCache.OfType<RootGroup>();
See http://www.thinqlinq.com/default/Using-Cast-Or-OfType-Methods.aspx
This has various niceties associated with deferred execution, but without more detail I can't really tell if that makes a difference. And like other posts noted, your information associated with the child class is not lost, you just need to put it in a form where its accessible.

Categories