2

I'm trying to convert a list of objects to a dictionary using the following code:

 var MyDictionary = MyList.Distinct().ToDictionary(i => i.ObjectId, i => i);

I know that a dictionary should not contain duplicate elements, hence the .Distinct(). Yet I still get the following Exception whenever there's a duplicate element:

An item with the same key has already been added.

MyList is a list of MyObject that looks like this:

public class MyObject{

        public string ObjectId { get; set; }

        public string FName { get; set; }

        public string LName { get; set; }

    }

Is there a better way to create a dictionary from a list of objects ? or am I doing something wrong?

Varda Elentári
  • 2,242
  • 6
  • 35
  • 55
  • 6
    I'm guessing you want to group by `ObjectId` instead of calling `Distinct`. Either you have multiple references to the same object or multiple objects that have the same `ObjectId`. – D Stanley Jul 11 '16 at 16:11
  • 1
    i.e, just because your `MyObject` do not compare equal (.Distinct) does not mean that their `ObjectIds` aren't equal. – RJFalconer Jul 11 '16 at 16:12
  • @DStanley which solution would be more efficient? using a groupBy or custom comparer with Distinct() ? – Varda Elentári Jul 11 '16 at 16:17
  • @MrX neither is more efficient, they do two different jobs. – Scott Chamberlain Jul 11 '16 at 16:18
  • 1
    @MrX You'd have to try it both ways and measure to be sure, but I wouldn't expect that there would be a huge performance difference to the overall performance of the system. The main advantage of a custom comparer is that it's _reusable_. So if you do this in many places it simplifies the code. – D Stanley Jul 11 '16 at 16:18
  • [C# Distinct by propery](http://stackoverflow.com/questions/489258/linqs-distinct-on-a-particular-property) contains plenty of ways to actually do `Distinct` you are looking for. – Alexei Levenkov Jul 11 '16 at 16:28
  • @DStanley this makes sense, Thanks. I did test it out of a fairly large dataset and it hardly had any difference in performance. For the sake of re-usability and readability I will use a custom comparer. – Varda Elentári Jul 11 '16 at 16:30

3 Answers3

4

If you want to compare on the ObjectId, you'll need to pass in a custom comparer to .Distinct(). You can do so like this:

class MyObjectComparer : IEqualityComparer<MyObject>
{
    public bool Equals(MyObject x, MyObject y)
    {
        return x.ObjectId == y.ObjectId;
    }

    public int GetHashCode(MyObject obj)
    {
        return obj.ObjectId.GetHashCode();
    }
}

var MyDictionary = MyList
    .Distinct(new MyObjectComparer())
    .ToDictionary(i => i.ObjectId, i => i);
itsme86
  • 19,266
  • 4
  • 41
  • 57
3

You could use Group by and then select first from the List as below:

var MyDictionary = MyList.GroupBy(i => i.ObjectId, i => i).ToDictionary(i => i.Key, i => i.First());
D_Learning
  • 1,033
  • 8
  • 18
  • 1
    You don't need the `i => i` parameter as the `GroupBy` that only takes one lambda does that by default. – juharr Jul 11 '16 at 16:25
  • Yes thats true, its not needed, but I wrote it just in case if User wants to select a specific variable in place of the whole object. So they could use the second argument in the Group by to select a specific variable or full object like: **MyList.GroupBy(i => i.ObjectId, i => (i.FName + i.LName))** – D_Learning Jul 11 '16 at 16:31
1

Distinct works using the objects built in Equals and GetHashCode methods by default but your dictionary works only over the id. You need to pass in a IEqualityComparer in to distinct that does the comparison on Id to test if items are equal or make MyObject implment Equals and GetHashCode and have that compare on the Id.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431