Reputation: 1272
I've got this class DNS_Log
that has four properties. I've created a list of these objects that I am trying to filter though for only distinct occurrences. (When the list is being populated, there are a lot of repeats)
Here's the listing being populated:
dnsLogs.Add( new DNS_Log { Destination = destination,
Source_IP = sourceIp,
Domain_Controller = domainController,
DateTime = datetime });
Here's my attempt at trying to filter out the distinct ones only:
dnsLogs = dnsLogs.Distinct().ToList();
Why does this not work? Do I need some linq expression in the distinct param? I want to compare the the objects as a whole on their properties. Is there any easier way of doing this?
P.S. I've played around with making a custom IEqualityComparer<DNS_Log>
that seems to work fine but I don't know how to implement it in this scenario.
Upvotes: 5
Views: 2439
Reputation: 460208
Why does this not work?
It does not work because Distinct
uses GetHashCode
+ Equals
to compare objects. Since you haven't overridden these methods from object you get the default implementation which just compares references.
So you have several options:
IEqualityComparer<T>
for the overload of Distinct
Equals
+ GethashCode
in your classAnother (less efficient) approach which doesn't need to create a new class or modify an existing is to use the builtin GetHashCode
+Equals
of an anonymous type and Enumerable.GroupBy
:
IEnumerable<DNS_Log> distinctLogs =
from dns in dnsLogs
group dns by new { dns.Destination, dns.Source_IP,dns.Domain_Controller, dns.DateTime } into g
select g.First(); // change logic if you don't want an arbitrary object(first)
Here's an example of the second approach:
public class DNS_Log
{
public string Destination{ get; set; }
public int Source_IP { get; set; }
public string Domain_Controller { get; set; }
public DateTime DateTime { get; set; }
public override bool Equals(object obj)
{
DNS_Log c2 = obj as DNS_Log;
if (obj == null) return false;
return Destination == c2.Destination && Source_IP == c2.Source_IP
&& Domain_Controller == c2.Domain_Controller && DateTime == c2.DateTime;
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + Destination.GetHashCode();
hash = hash * 23 + Source_IP;
hash = hash * 23 + Domain_Controller.GetHashCode();
hash = hash * 23 + DateTime.GetHashCode();
return hash;
}
}
}
The Equals
and GethashCode
of the IEqualityComparer
-class (1. approach) would be similar.
Upvotes: 1
Reputation: 391456
You've got several options:
DNS_Log
IEquatable<T>
NOTE! In all the code below, the equality check assumes that the ==
operator knows how to deal with each type. That is certainly true for the DateTime member (assuming it is of type DateTime as well), but I obviously cannot guarantee that the others will work. If the Destination member holds a type for which the ==
operator has not been defined, this is likely to do the wrong thing. Since you haven't posted your own code for this comparer implementation, it's impossible to know what to do here.
IEquatable<T>
public class DNS_Log : IEquatable<DNS_Log>
{
public bool Equals(DNS_Log other)
{
if (other == null)
return false;
return (other.Destination == Destination
&& other.Source_IP == Source_IP
&& other.Domain_Controller == Domain_Controller
&& other.DateTime == DateTime);
}
public override int GetHashCode()
{
int hash = 23;
hash = hash * 59 + (Destination == null ? 0 : Destination.GetHashCode());
hash = hash * 59 + (Source_IP == null ? 0 : Source_IP.GetHashCode());
hash = hash * 59 + (Domain_Controller == null ? 0 : Domain_Controller.GetHashCode());
hash = hash * 59 + DateTime.GetHashCode();
return hash;
}
}
public class DNS_Log
{
public override bool Equals(object obj)
{
if (obj == null) return false;
var other = obj as DNS_Log;
if (other == null) return false;
... rest the same as above
IEqualityComparer<T>
Lastly, you can provide an IEqualityComparer<T> when calling Distinct
:
dnsLogs = dnsLogs.Distinct(new DNS_LogEqualityComparer()).ToList();
public class DNS_LogEqualityComparer : IEqualityComparer<DNS_Log>
{
public int GetHashCode(DNS_Log obj)
{
int hash = 23;
hash = hash * 59 + (obj.Destination == null ? 0 : obj.Destination.GetHashCode());
hash = hash * 59 + (obj.Source_IP == null ? 0 : obj.Source_IP.GetHashCode());
hash = hash * 59 + (obj.Domain_Controller == null ? 0 : obj.Domain_Controller.GetHashCode());
hash = hash * 59 + obj.DateTime.GetHashCode();
return hash;
}
public bool Equals(DNS_Log x, DNS_Log y)
{
if (ReferenceEquals(x, y)) return true;
if (x == null) return false;
return (x.Destination == y.Destination
&& x.Source_IP == y.Source_IP
&& .Domain_Controller == y.Domain_Controller
&& x.DateTime == y.DateTime);
}
}
Upvotes: 6
Reputation: 5402
You basically have three options:
IEqualityComparer<DNS_Log>
when calling Distinct
(something like dnsLogs.Distinct(myEqualityComparerInstance).ToList();
DNS_Log
implement IEquatable<DNS_Log>
Equals
and GetHashCode
on DNS_Log
Upvotes: 2