1

I am trying to use DISTINCT on my list stationList but it seems like I'm doing something wrong because I don't get distinct list. How would I get distinct ones in Value where I'm setting stationList.

foreach (StationCategory stationCategory in productCatalog.Programming.StationCategory)
    {
       StringBuilder stationList = new StringBuilder();
       foreach (Station station in stationCategory.Station.Distinct())
       {
          stationList.Append(station.StationName + ",");
       }
       offer.FeatureList.Add(new Feature() { FeatureName = "<b>" + stationCategory.CategoryName + "</b>", Value = stationList.ToString().TrimEnd(',') });
    }
zaria khan
  • 333
  • 3
  • 8
  • 18
  • 1
    You don't get what? What is `Station`, what does it contain and how do you compare for equality? Please post the *relevant* code, what you posted shows iteration over categories. Post code that actually reproduces the issue – Panagiotis Kanavos Nov 11 '16 at 08:07
  • to apply distinct you need a criterion to define distinctiveness. have a look on this [post](http://stackoverflow.com/questions/10255121/get-a-list-of-distinct-values-in-list). I suggest to try the `GroupBy` answer in the post – Mong Zhu Nov 11 '16 at 08:09
  • If stations can be duplicate accross different categories you'll want to replace the 2 `foreach` loops with one `productCatalog.Programming.StationCategory.SelectMany(sc => sc.Station).Distinct()` – KMoussa Nov 11 '16 at 08:11

6 Answers6

4

Your current code does work, but it takes the distinct object references, instead of the names you expect. To do that, you have to tell the code how to match the Distinct.

There is a problem though, since Distinct doesn't take a lambda, so you can't say 'give me the distinct station names. In order to do that, you have to group, like in this sample:

var ds = stationCategory.Station.GroupBy(s => s.StationName)
                                .Select(g => g.FirstOrDefault());

ds now holds an enumerable of stations. You can take the name from it. The other properties are not guaranteed to be unique. (If you are only interested in the names, you could use this Select: .Select(g => g.FirstOrDefault()?.StationName);)

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
3

Your code will work if you override Equals method in Station class as below

public override bool Equals(object obj)
{
   return StationName == ((Station)obj).StationName;
} 

otherwise it will compare whole station object with another station object and it will give incorrect results. Alternative approach is you can take the distincts of Station names list like stationCategory.Station.Select(x=>x.StationName).Distinct() or using GroupBy

example:

stationList.Append(string.Join(",",
                    stationCategory.Station.GroupBy(b=>b.StationName).Select(g=>g.Key).ToArray()));
Damith
  • 62,401
  • 13
  • 102
  • 153
  • 2
    @PatrickHofman Updated, Time for SO is limited. Agreed that I haven't explained it clearly. Thanks for the reminder. – Damith Nov 11 '16 at 09:06
0

I assume this is because the Distinct function is comparing the object references not the contents as this is the default equality comparer

Returns distinct elements from a sequence by using the default equality comparer to compare values.

https://msdn.microsoft.com/en-us/library/bb348436(v=vs.110).aspx

You can either have your Station implement the IEqualityComparer<Station> inferface or implement an IEqualityComparer.

Oliver
  • 35,233
  • 12
  • 66
  • 78
0

Update this code to existing one :

    foreach (StationCategory stationCategory in productCatalog.Programming.StationCategory)
     {
        StringBuilder stationList = new StringBuilder();
        foreach (var stationName in stationCategory.Station.GroupBy(x => x.Name).Select(x => x.First()))
         {
           stationList.Append(stationName + ",");
         }
         offer.FeatureList.Add(new Feature() { FeatureName = "<b>" + stationCategory.CategoryName + "</b>", Value = stationList.ToString().TrimEnd(',')});
     }
  • 1
    What have you changed? How does this help the OP? What did he do wrong? – Patrick Hofman Nov 11 '16 at 08:16
  • 2
    unfortunately this version will only save the full name of the class `Station`. Please look at the [edit history](http://stackoverflow.com/posts/40543678/revisions) Ivan Stoev' s edit made it work. Now you reversed it again.In here: `stationList.Append(stationName + ",");` `stationName` is not a `string` but a `Station` – Mong Zhu Nov 11 '16 at 08:37
0

.Distinct checks equality of objects due to GetHashCode and Equals methods. If you do not override it at Station class it will never perform correct filter.

If you have overriden GetHashCode and Equals methods at Station class then you should fix them, because they are not returning correct values.

Another way - without overrides is to collect a List<string> which will contains your station names and call .Distinct on it as following:

foreach (StationCategory stationCategory in productCatalog.Programming.StationCategory)
{
   var stationList = new List<string>();
   foreach (Station station in stationCategory.Station)
   {
      stationList.Add(station.StationName);
   }
   offer.FeatureList.Add(new Feature() 
    { 
        FeatureName = "<b>" + stationCategory.CategoryName + "</b>", 
        Value = string.Join(", ", stationList.Distinct()) 
    });
}
Mikhail Tulubaev
  • 4,141
  • 19
  • 31
0

You're currently using the default equality comparer to find distinct values.

To compare a custom data type, you need to implement this interface and provide your own GetHashCode and Equals methods for the type.

For more info and an example see here: https://msdn.microsoft.com/en-us/library/bb348436(v=vs.110).aspx