4

I have an issue using distinct in LINQ. I have this list:

LineIdChanged   LineId  OldGatewayPCId  NewGatewayPCId  LineStringID    PlantID
1               93      83              88              160             2
2               93      83              88              161             2
3               94      82              87              162             2
4               94      82              87              163             2

What I have tried is to get a distinct LineId value, so in this case I should only get two objects instead of all four objects. I have tried this:

  var s = (from n in _dataBaseProvider.SelectPjdGatewayLineChanged(selectedSourcePlant.LPS_Database_ID)
          select new PjdGatewayLineChanged() { LineId = n.LineId, LpsLineNo = n.LpsLineNo, LineIdChanged = n.LineIdChanged}).Distinct();

  LinesOld = s.ToList();

But this gives me all 4 objects.

PaulG
  • 13,871
  • 9
  • 56
  • 78
mortenstarck
  • 2,713
  • 8
  • 43
  • 76
  • You could look at using GroupBy to help you here. e.g. http://stackoverflow.com/questions/7325278/group-by-in-linq or http://stackoverflow.com/questions/5231845/c-sharp-linq-group-by-on-multiple-columns – Jason Evans Aug 05 '13 at 11:47
  • 1
    You get all 4 rows since `LineIdChanged` is different for each row. Same problem may be with `LpsLineNo` which I cannot see in your described data. – NoLifeKing Aug 05 '13 at 11:51
  • What is `LpsLineNo`? @NoLifeKing is right. You will get 4 rows as the fields you need to fetch, are unique, collectively. – Nayan Aug 05 '13 at 11:54

4 Answers4

3

You need to use MoreLINQ's DistinctBy:

var s = 
    (from n in _dataBaseProvider.SelectPjdGatewayLineChanged
    (selectedSourcePlant.LPS_Database_ID)
    select new PjdGatewayLineChanged
    { 
        LineId = n.LineId,
        LpsLineNo = n.LpsLineNo, 
        LineIdChanged = n.LineIdChanged
    })
    .DistinctBy(p => p.LineId);
Dave Bish
  • 19,263
  • 7
  • 46
  • 63
It'sNotALie.
  • 22,289
  • 12
  • 68
  • 103
  • I can't indent this monstrosity... :( – It'sNotALie. Aug 05 '13 at 11:47
  • 2
    Alternatively, `.GroupBy(p => p.LineId).Select(x => x.First())`, which, IIRC, is basically what MoreLinq does under the hood (minus the clever streaming stuff). – anaximander Aug 05 '13 at 11:50
  • @anaximander Not really, it uses a Hashset so no need for the grouping and the select. – It'sNotALie. Aug 05 '13 at 11:53
  • 1
    It seems my recollection is fuzzy. It uses the method I stated on platforms where hashsets are not available. [The source is available](https://code.google.com/p/morelinq/source/browse/MoreLinq/DistinctBy.cs) so if you don't want to include an entire library you can just use the implementation of this function (being sure to attribute it, of course). – anaximander Aug 05 '13 at 12:02
0

what your problem is that the LINQ and the .Net framework dosent know how to differentiate betwwen different objects of type PjdGatewayLineChanged. so it uses the default thing which is to look for equality in the terms of memory reference.

So what you need to do instead is use the second overload of this method and provide an IEqualityComparer

see here

Distinct<TSource>(IEnumerable<TSource>, IEqualityComparer<TSource>)

so that LINQ known how to compare diff instances of type PjdGatewayLineChanged
msdn link of IEqualityComparer

Parv Sharma
  • 12,581
  • 4
  • 48
  • 80
0

If you consider all the rows to be equal if they have some fields equal:

var s = (from p in _dataBaseProvider.SelectPjdGatewayLineChanged(selectedSourcePlant.LPS_Database_ID)
    select new PjdGatewayLineChanged() 
    { 
        LineId = p.LineId,
        LpsLineNo = p.LpsLineNo, 
        LineIdChanged = p.LineIdChanged
    })
    .GroupBy(p => p.LineId)
    .Select(p => p.First());

You group-by by your id and then for each group take the first row.

or, more compact,

var s = from p in _dataBaseProvider.SelectPjdGatewayLineChanged(selectedSourcePlant.LPS_Database_ID)
    group p by p.LineId into q
    let r = q.First()
    select new PjdGatewayLineChanged() 
    { 
        LineId = r.LineId,
        LpsLineNo = r.LpsLineNo, 
        LineIdChanged = r.LineIdChanged
    };

In this way the creation of PjdGatewayLineChanged has been moved to the last step (after the selection of the right "candidate" of the groupby.

xanatos
  • 109,618
  • 12
  • 197
  • 280
0

Here is the thing. The Distinct(IEnumerable) method returns an unordered sequence that contains no duplicate values. It uses the default equality comparer, Default, to compare values.

There are two options to use .Distinct() with your custom types:

  1. provide your own GetHashCode and Equals methods for the PjdGatewayLineChanged.
  2. use overloaded Distinct with your custom Equality Comparer

For more info check MSDN for Distinctcheck MSDN for Distinct, you'll find good documentation code snippets which will help you implement needed functionality.

Val
  • 529
  • 4
  • 13