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();
}
}
Related
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.
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.
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.
I have a List<MyObject> allObjects and List<MyObject> someObjects (all of the objects in someObjects belongs to allObjects too. I want to get the elements from allObjects which doesn't belong to someObjects ? How can I achieve that with LINQ ?
It's as easy as allObjects.Except(someObjects)
However, you should be aware that this uses the default equality comparer under the covers to compare the values.
If you wish to use a custom IEqualityComparer<MyObject>, there's an overload that allows you to do just that.
var exceptionList= allObjects.Except(someObjects);
Try this
allObjects.Except(someObjects)
I have a List<T> where T is a custom object. None of my object are equal but some might have an equal property. Is there any fast way to remove the duplicates by comparing the property? It doesn't matter which of the duplicates stays in the list.
You can use List<T>.RemoveAll to do this efficiently.
For example, if you wanted to remove all elements where the Foo property had a value of 42, you could do:
theList.RemoveAll(i => i.Foo == 42);
If you're trying to make a list of distinct items by a property, ie: keep only distinct Foo items, I would recommend doing something like:
HashSet<int> elements = new HashSet<int>(); // Type of property
theList.RemoveAll(i => !elements.Add(i.Foo));
This will track which elements are "distinct" and remove all others.
Group the objects based on the property value, then pick the first item in each group. Like this:
var distinctObjects = objects
.GroupBy(x => x.Property)
.Select(g => g.First());
You can create a new class that implements IEqualityComparer<T> by comparing the property. Then you can use linq's Distinct method to get an IEnumerable that contains only the unique elements.
you can also use a very good library from here http://powercollections.codeplex.com/ and use Algorithms.RemoveDuplicates method. That library has many more other goodies on collections.