6

I'm populating an observable collection with the following:

var customers = new ObservableCollection<Customer>();

foreach (
    var customer in
        collListItem.Select(
            item =>
            new Customer
                {
                    Persona = item["Persona"].ToString(),
                    CustomerName = item["Title"].ToString()
                }))
{
    customers.Add(customer);
}

Before I populate a WPF datagrid with the elements from this collection I'd like to make it a unique list of personas and customers (no duplicate rows).

I tried to use the following for this:

customers = customers.Distinct();

However I received the error:

Cannot convert source type 'System.Collections.Generic.IEnumerable to target type 'System.Collections.ObjectModel.ObservableCollection

Is there an equivalent for an ObservableCollection that I can use?

Michael A
  • 9,480
  • 22
  • 70
  • 114
  • Put the `.Distinct()` after your select. Also, make sure `Customer` properly implements its own `GetHashCode()` and `Equals` methods to compare the `Persona` and `CustomerName` – Rob Aug 18 '15 at 04:46
  • You can make `customers` distinct only with respect to `Persona` or `CustomerName`. – NASSER Aug 18 '15 at 04:49
  • Look at here: http://stackoverflow.com/questions/489258/linq-distinct-on-a-particular-property – NASSER Aug 18 '15 at 04:51

3 Answers3

6

Because Distinct returns an IEnumerable<T>, which is not an ObservableCollection.

If you want to distinct ObservableCollection you should create it again like this:

customers = new ObservableCollection<Customer>(customers.Distinct());

Or, as variant, you can modify your query and distinct at once:

foreach (
var customer in
    collListItem.Select(
        item =>
        new Customer
            {
                Persona = item["Persona"].ToString(),
                CustomerName = item["Title"].ToString()
            }).Distinct())
{
    customers.Add(customer);
}
Ben
  • 54,723
  • 49
  • 178
  • 224
Artem Kulikov
  • 2,250
  • 19
  • 32
  • First part of this solution looks correct but the second doesn't appear to function as expected. – Michael A Aug 18 '15 at 05:10
  • 1
    @Codingo You need to implement `Equals` and `GetHashCode` in your `Customer` class, or it won't work as you expect. Otherwise, it will get unique *instances* (which they all are - even if they have the same values). Alternatively, you can use an anonymous type, which will behave as expected when you use `.Distinct()` – Rob Aug 18 '15 at 05:26
5

Well this is an old question but I am just facing the same issue and I think I found an alternative way to do what the OP is attempting. First of all I think we are facing here an XY problem. The OP wants to hold an ObservableCollections with unique items, now, both answers existing answers solve the problem in a way, but I think this way is not the best.

This should be the responsibility of the data structure we are using. If that data structure does not exist let's create it!

The requirement is quite clear: To have an ObservableCollection with unique items. The way I foudn was to inherit from ObservableCollection and provide custom logic to do this:

public class ObservableUniqueCollection <T> : ObservableCollection<T>
{
    private readonly HashSet<T> _hashSet;

    public ObservableUniqueCollection() : this(EqualityComparer<T>.Default) { }

    public ObservableUniqueCollection(IEqualityComparer<T> equalityComparer) => _hashSet = new HashSet<T>(equalityComparer);

    public void AddRange(IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            InsertItem(Count, item);
        }
    }

    protected override void InsertItem(int index, T item)
    {
        if (_hashSet.Add(item))
        {
            base.InsertItem(index, item);
        }
    }

    protected override void ClearItems()
    {
        base.ClearItems();
        _hashSet.Clear();
    }

    protected override void RemoveItem(int index)
    {
        var item = this [index];
        _hashSet.Remove(item);
        base.RemoveItem(index);
    }

    protected override void SetItem(int index, T item)
    {
        if (_hashSet.Add(item))
        {
            var oldItem = this[index];
            _hashSet.Remove(oldItem);
            base.SetItem(index, item);
        }
    }
}

So now you don't have to worry about having repeated items in your collection (as long as your type implements IEquatable or you provide an IEqualityComparer)

taquion
  • 2,667
  • 2
  • 18
  • 29
2

This will work without having to implement your own compare on Customer

foreach (var customer in collListItem.Select(
            item => 
                new {
                    Persona = item["Persona"].ToString(),
                    CustomerName = item["Title"].ToString()
                }).Distinct()
                .Select(r => new Customer { Persona = r.Persona,
                                            CustomerName = r.CustomerName }))
{
    customers.Add(customer);
}
Rob
  • 26,989
  • 16
  • 82
  • 98