Remove objects with duplicate properties from List - c#

I have a List of objects in C#. All of the objects contain the properties dept and course.
There are several objects that have the same dept and course.
How can I trim the List(or make a new List) where there is only one object per unique (dept & course) properties.
[Any additional duplicates are dropped out of the List]
I know how to do this with a single property:
fooList.GroupBy(x => x.dept).Select(x => x.First());
However, I am wondering how to do this for multiple properties (2 or more)?

To use multiple properties you can use an anonymous type:
var query = fooList.GroupBy(x => new { x.Dept, x.Course })
.Select(x => x.First());
Of course, this depends on what types Dept and Course are to determine equality. Alternately, your classes can implement IEqualityComparer<T> and then you could use the Enumerable.Distinct method that accepts a comparer.

Another approach is to use the LINQ Distinct extension method together with an IEqualityComparer<Foo>. It requires you to implement a comparer; however, the latter is reusable and testable.
public class FooDeptCourseEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
return
x.Dept == y.Dept &&
x.Course.ToLower() == y.Course.ToLower();
}
public int GetHashCode(Foo obj)
{
unchecked {
return 527 + obj.Dept.GetHashCode() * 31 + obj.Course.GetHashCode();
}
}
#region Singleton Pattern
public static readonly FooDeptCourseEqualityComparer Instance =
new FooDeptCourseEqualityComparer();
private FooDeptCourseEqualityComparer() { }
#endregion
}
My example uses the singleton pattern. Since the class does not have any state information, we do not need to create a new instance each time we use it.
My code does not handle null values. Of course you would have to handle them, if they can occur.
The unique values are returned like this
var result = fooList.Distinct(FooDeptCourseEqualityComparer.Instance);
UPDATE
I suggest using a generic EqualityComparer class that accepts lambda expressions in the constructor and can be reused in multiple situations
public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, T, bool> _areEqual;
private Func<T, int> _getHashCode;
public LambdaEqualityComparer(Func<T, T, bool> areEqual,
Func<T, int> getHashCode)
{
_areEqual = areEqual;
_getHashCode = getHashCode;
}
public LambdaEqualityComparer(Func<T, T, bool> areEqual)
: this(areEqual, obj => obj.GetHashCode())
{
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return _areEqual(x, y);
}
public int GetHashCode(T obj)
{
return _getHashCode(obj);
}
#endregion
}
You can use it like this
var comparer = new LambdaEqualityComparer<Foo>(
(x, y) => x.Dept == y.Dept && x.Course == y.Course,
obj => {
unchecked {
return 527 + obj.Dept.GetHashCode() * 31 + obj.Course.GetHashCode();
}
}
);
var result = fooList.Distinct(comparer);
Note: You have to provide a calculation of the hash code, since Distinct uses an internal Set<T> class, which in turn uses hash codes.
UPDATE #2
An even more generic equality comparer implements the comparison automatically and accepts a list of property accessors; however, you have no control, on how the comparison is performed.
public class AutoEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, object>[] _propertyAccessors;
public AutoEqualityComparer(params Func<T, object>[] propertyAccessors)
{
_propertyAccessors = propertyAccessors;
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
foreach (var getProp in _propertyAccessors) {
if (!getProp(x).Equals(getProp(y))) {
return false;
}
}
return true;
}
public int GetHashCode(T obj)
{
unchecked {
int hash = 17;
foreach (var getProp in _propertyAccessors) {
hash = hash * 31 + getProp(obj).GetHashCode();
}
return hash;
}
}
#endregion
}
Usage
var comparer = new AutoEqualityComparer<Foo>(foo => foo.Dept,
foo => foo.Course);
var result = fooList.Distinct(comparer);

Related

Get list of distinct values in List<T> in c#

So, say I have something like the following:
public class Element
{
public int ID;
public int Type;
public Properties prorerty;
...
}
and
public class Properties
{
public int Id;
public string Property;
...
}
and I have a list of these:
List Elements = new List();
What would be the cleanest way to get a list of all distinct values in the prorerty column in Element class? I mean, I could iterate through the list and add all values that aren't duplicates to another list of strings, but this seems dirty and inefficient. I have a feeling there's some magical Linq construction that'll do this in one line, but I haven't been able to come up with anything.
var results = Elements.Distinct();
Note: you will have to override .Equals and .GetHashCode()
public class Element : IEqualityComparer<Element>
{
public bool Equals(Element x, Element y)
{
if (x.ID == y.ID)
{
return true;
}
else
{
return false;
}
}
}
public int GetHashCode(Element obj)
{
return obj.ID.GetHashCode();
}
Isn't simpler to use one of the approaches shown below :) ? You can just group your domain objects by some key and select FirstOrDefault like below.
This is a copy of my answer on similar question here:
Get unique values - original answer
More interesting option is to create some Comparer adapter that takes you domain object and creates other object the Comparer can use/work with out of the box. Base on the comparer you can create your custom linq extensions like in sample below. Hope it helps :)
[TestMethod]
public void CustomDistinctTest()
{
// Generate some sample of domain objects
var listOfDomainObjects = Enumerable
.Range(10, 10)
.SelectMany(x =>
Enumerable
.Range(15, 10)
.Select(y => new SomeClass { SomeText = x.ToString(), SomeInt = x + y }))
.ToList();
var uniqueStringsByUsingGroupBy = listOfDomainObjects
.GroupBy(x => x.SomeText)
.Select(x => x.FirstOrDefault())
.ToList();
var uniqueStringsByCustomExtension = listOfDomainObjects.DistinctBy(x => x.SomeText).ToList();
var uniqueIntsByCustomExtension = listOfDomainObjects.DistinctBy(x => x.SomeInt).ToList();
var uniqueStrings = listOfDomainObjects
.Distinct(new EqualityComparerAdapter<SomeClass, string>(x => x.SomeText))
.OrderBy(x=>x.SomeText)
.ToList();
var uniqueInts = listOfDomainObjects
.Distinct(new EqualityComparerAdapter<SomeClass, int>(x => x.SomeInt))
.OrderBy(x => x.SomeInt)
.ToList();
}
Custom comparer adapter:
public class EqualityComparerAdapter<T, V> : EqualityComparer<T>
where V : IEquatable<V>
{
private Func<T, V> _valueAdapter;
public EqualityComparerAdapter(Func<T, V> valueAdapter)
{
_valueAdapter = valueAdapter;
}
public override bool Equals(T x, T y)
{
return _valueAdapter(x).Equals(_valueAdapter(y));
}
public override int GetHashCode(T obj)
{
return _valueAdapter(obj).GetHashCode();
}
}
Custom linq extension (definition of DistinctBy extension method):
// Embed this class in some specific custom namespace
public static class DistByExt
{
public static IEnumerable<T> DistinctBy<T,V>(this IEnumerable<T> enumerator,Func<T,V> valueAdapter)
where V : IEquatable<V>
{
return enumerator.Distinct(new EqualityComparerAdapter<T, V>(valueAdapter));
}
}
Definition of domain class used in test case:
public class SomeClass
{
public string SomeText { get; set; }
public int SomeInt { get; set; }
}
var props = Elements.Select(x => x.Properties).Distinct();
And make sure you overridden .Equals() and .GetHashCode() methods.
Or if you need direct strings from Properties:
var props = Elements
.Select(x => x.Properties != null ? x.Properties.Property : null)
.Distinct();
If you need the string fields on the Properties field, and if you know the Properties field prorerty is never null, just use
IEnumerable<string> uniqueStrings = Elements
.Select(e => e.prorerty.Property).Distinct();
If there's a chance prorerty can be null, handle that situation in the lambda.
This will use the default equality comparer for String which is an ordinal comparison independent of culture and case-sensitive.
my working example from LINQPad (C# Program)
void Main()
{
var ret = new List<Element>();
ret.Add(new Element(){ID=1});
ret.Add(new Element(){ID=1});
ret.Add(new Element(){ID=2});
ret = ret.GroupBy(x=>x.ID).Select(x=>x.First()).ToList();
Console.WriteLine(ret.Count()); // shows 2
}
public class Element
{
public int ID;
public int Type;
public Properties prorerty;
}
public class Properties
{
public int Id;
public string Property;
}

Filtering duplicates out of an IEnumerable

I have this code:
class MyObj {
int Id;
string Name;
string Location;
}
IEnumerable<MyObj> list;
I want to convert list to a dictionary like this:
list.ToDictionary(x => x.Name);
but it tells me I have duplicate keys. How can I keep only the first item for each key?
I suppose the easiest way would be to group by key and take the first element of each group:
list.GroupBy(x => x.name).Select(g => g.First()).ToDictionary(x => x.name);
Or you could use Distinct if your objects implement IEquatable to compare between themselves by key:
// I'll just randomly call your object Person for this example.
class Person : IEquatable<Person>
{
public string Name { get; set; }
public bool Equals(Person other)
{
if (other == null)
return false;
return Name == other.Name;
}
public override bool Equals(object obj)
{
return base.Equals(obj as Person);
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
...
list.Distinct().ToDictionary(x => x.Name);
Or if you don't want to do that (maybe because you normally want to compare for equality in a different way, so Equals is already in use) you could make a custom implementation of IEqualityComparer just for this case:
class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null)
return y == null;
if (y == null)
return false;
return x.Name == y.Name;
}
public int GetHashCode(Person obj)
{
return obj.Name.GetHashCode();
}
}
...
list.Distinct(new PersonComparer()).ToDictionary(x => x.Name);
list.Distinct().ToDictionary(x => x.Name);
You could also create your own Distinct extension overload method that accepted a Func<> for choosing the distinct key:
public static class EnumerationExtensions
{
public static IEnumerable<TSource> Distinct<TSource,TKey>(
this IEnumerable<TSource> source, Func<TSource,TKey> keySelector)
{
KeyComparer comparer = new KeyComparer(keySelector);
return source.Distinct(comparer);
}
private class KeyComparer<TSource,TKey> : IEqualityComparer<TSource>
{
private Func<TSource,TKey> keySelector;
public DelegatedComparer(Func<TSource,TKey> keySelector)
{
this.keySelector = keySelector;
}
bool IEqualityComparer.Equals(TSource a, TSource b)
{
if (a == null && b == null) return true;
if (a == null || b == null) return false;
return keySelector(a) == keySelector(b);
}
int IEqualityComparer.GetHashCode(TSource obj)
{
return keySelector(obj).GetHashCode();
}
}
}
Apologies for any bad code formatting, I wanted to reduce the size of the code on the page. Anyway, you can then use ToDictionary:
var dictionary = list.Distinct(x => x.Name).ToDictionary(x => x.Name);
Could make your own perhaps? For example:
public static class Extensions
{
public static IDictionary<TKey, TValue> ToDictionary2<TKey, TValue>(
this IEnumerable<TValue> subjects, Func<TValue, TKey> keySelector)
{
var dictionary = new Dictionary<TKey, TValue>();
foreach(var subject in subjects)
{
var key = keySelector(subject);
if(!dictionary.ContainsKey(key))
dictionary.Add(key, subject);
}
return dictionary;
}
}
var dictionary = list.ToDictionary2(x => x.Name);
Haven't tested it, but should work. (and it should probably have a better name than ToDictionary2 :p)
Alternatively, you can implement a DistinctBy method, for example like this:
public static IEnumerable<TSubject> DistinctBy<TSubject, TValue>(this IEnumerable<TSubject> subjects, Func<TSubject, TValue> valueSelector)
{
var set = new HashSet<TValue>();
foreach(var subject in subjects)
if(set.Add(valueSelector(subject)))
yield return subject;
}
var dictionary = list.DistinctBy(x => x.Name).ToDictionary(x => x.Name);
The problem here is that the ToDictionary extension method does not support multiple values with the same key. One solution is to write a version which does and use that instead.
public static Dictionary<TKey,TValue> ToDictionaryAllowDuplicateKeys<TKey,TValue>(
this IEnumerable<TValue> values,
Func<TValue,TKey> keyFunc) {
var map = new Dictionary<TKey,TValue>();
foreach ( var cur in values ) {
var key = keyFunc(cur);
map[key] = cur;
}
return map;
}
Now converting to a dictionary is straight forward
var map = list.ToDictionaryAllowDuplicateKeys(x => x.Name);
The following will work if you have different instances of MyObj with the same value for the Name property. It will take the first instance found for each duplicate (sorry for the obj - obj2 notation, it is just sample code):
list.SelectMany(obj => new MyObj[] {list.Where(obj2 => obj2.Name == obj.Name).First()}).Distinct();
EDIT: Joren's solution is better as it does not create unnecessary arrays in the process.

Distinct() with lambda?

Right, so I have an enumerable and wish to get distinct values from it.
Using System.Linq, there's, of course, an extension method called Distinct. In the simple case, it can be used with no parameters, like:
var distinctValues = myStringList.Distinct();
Well and good, but if I have an enumerable of objects for which I need to specify equality, the only available overload is:
var distinctValues = myCustomerList.Distinct(someEqualityComparer);
The equality comparer argument must be an instance of IEqualityComparer<T>. I can do this, of course, but it's somewhat verbose and, well, cludgy.
What I would have expected is an overload that would take a lambda, say a Func<T, T, bool>:
var distinctValues = myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);
Anyone know if some such extension exists, or some equivalent workaround? Or am I missing something?
Alternatively, is there a way of specifying an IEqualityComparer inline (embarrass me)?
Update
I found a reply by Anders Hejlsberg to a post in an MSDN forum on this subject. He says:
The problem you're going to run into is that when two objects compare
equal they must have the same GetHashCode return value (or else the
hash table used internally by Distinct will not function correctly).
We use IEqualityComparer because it packages compatible
implementations of Equals and GetHashCode into a single interface.
I suppose that makes sense.
IEnumerable<Customer> filteredList = originalList
.GroupBy(customer => customer.CustomerId)
.Select(group => group.First());
It looks to me like you want DistinctBy from MoreLINQ. You can then write:
var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId);
Here's a cut-down version of DistinctBy (no nullity checking and no option to specify your own key comparer):
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> knownKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (knownKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
To Wrap things up . I think most of the people which came here like me want the simplest solution possible without using any libraries and with best possible performance.
(The accepted group by method for me i think is an overkill in terms of performance. )
Here is a simple extension method using the IEqualityComparer interface which works also for null values.
Usage:
var filtered = taskList.DistinctBy(t => t.TaskExternalId).ToArray();
Extension Method Code
public static class LinqExtensions
{
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
{
GeneralPropertyComparer<T, TKey> comparer = new GeneralPropertyComparer<T,TKey>(property);
return items.Distinct(comparer);
}
}
public class GeneralPropertyComparer<T,TKey> : IEqualityComparer<T>
{
private Func<T, TKey> expr { get; set; }
public GeneralPropertyComparer (Func<T, TKey> expr)
{
this.expr = expr;
}
public bool Equals(T left, T right)
{
var leftProp = expr.Invoke(left);
var rightProp = expr.Invoke(right);
if (leftProp == null && rightProp == null)
return true;
else if (leftProp == null ^ rightProp == null)
return false;
else
return leftProp.Equals(rightProp);
}
public int GetHashCode(T obj)
{
var prop = expr.Invoke(obj);
return (prop==null)? 0:prop.GetHashCode();
}
}
Shorthand solution
myCustomerList.GroupBy(c => c.CustomerId, (key, c) => c.FirstOrDefault());
No there is no such extension method overload for this. I've found this frustrating myself in the past and as such I usually write a helper class to deal with this problem. The goal is to convert a Func<T,T,bool> to IEqualityComparer<T,T>.
Example
public class EqualityFactory {
private sealed class Impl<T> : IEqualityComparer<T,T> {
private Func<T,T,bool> m_del;
private IEqualityComparer<T> m_comp;
public Impl(Func<T,T,bool> del) {
m_del = del;
m_comp = EqualityComparer<T>.Default;
}
public bool Equals(T left, T right) {
return m_del(left, right);
}
public int GetHashCode(T value) {
return m_comp.GetHashCode(value);
}
}
public static IEqualityComparer<T,T> Create<T>(Func<T,T,bool> del) {
return new Impl<T>(del);
}
}
This allows you to write the following
var distinctValues = myCustomerList
.Distinct(EqualityFactory.Create((c1, c2) => c1.CustomerId == c2.CustomerId));
Here's a simple extension method that does what I need...
public static class EnumerableExtensions
{
public static IEnumerable<TKey> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector)
{
return source.GroupBy(selector).Select(x => x.Key);
}
}
It's a shame they didn't bake a distinct method like this into the framework, but hey ho.
This will do what you want but I don't know about performance:
var distinctValues =
from cust in myCustomerList
group cust by cust.CustomerId
into gcust
select gcust.First();
At least it's not verbose.
From .NET 6 or later, there is a new build-in method Enumerable.DistinctBy to achieve this.
var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId);
// With IEqualityComparer
var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId, someEqualityComparer);
Something I have used which worked well for me.
/// <summary>
/// A class to wrap the IEqualityComparer interface into matching functions for simple implementation
/// </summary>
/// <typeparam name="T">The type of object to be compared</typeparam>
public class MyIEqualityComparer<T> : IEqualityComparer<T>
{
/// <summary>
/// Create a new comparer based on the given Equals and GetHashCode methods
/// </summary>
/// <param name="equals">The method to compute equals of two T instances</param>
/// <param name="getHashCode">The method to compute a hashcode for a T instance</param>
public MyIEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
{
if (equals == null)
throw new ArgumentNullException("equals", "Equals parameter is required for all MyIEqualityComparer instances");
EqualsMethod = equals;
GetHashCodeMethod = getHashCode;
}
/// <summary>
/// Gets the method used to compute equals
/// </summary>
public Func<T, T, bool> EqualsMethod { get; private set; }
/// <summary>
/// Gets the method used to compute a hash code
/// </summary>
public Func<T, int> GetHashCodeMethod { get; private set; }
bool IEqualityComparer<T>.Equals(T x, T y)
{
return EqualsMethod(x, y);
}
int IEqualityComparer<T>.GetHashCode(T obj)
{
if (GetHashCodeMethod == null)
return obj.GetHashCode();
return GetHashCodeMethod(obj);
}
}
All solutions I've seen here rely on selecting an already comparable field. If one needs to compare in a different way, though, this solution here seems to work generally, for something like:
somedoubles.Distinct(new LambdaComparer<double>((x, y) => Math.Abs(x - y) < double.Epsilon)).Count()
Take another way:
var distinctValues = myCustomerList.
Select(x => x._myCaustomerProperty).Distinct();
The sequence return distinct elements compare them by property '_myCaustomerProperty' .
You can use LambdaEqualityComparer:
var distinctValues
= myCustomerList.Distinct(new LambdaEqualityComparer<OurType>((c1, c2) => c1.CustomerId == c2.CustomerId));
public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
public LambdaEqualityComparer(Func<T, T, bool> equalsFunction)
{
_equalsFunction = equalsFunction;
}
public bool Equals(T x, T y)
{
return _equalsFunction(x, y);
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
private readonly Func<T, T, bool> _equalsFunction;
}
You can use InlineComparer
public class InlineComparer<T> : IEqualityComparer<T>
{
//private readonly Func<T, T, bool> equalsMethod;
//private readonly Func<T, int> getHashCodeMethod;
public Func<T, T, bool> EqualsMethod { get; private set; }
public Func<T, int> GetHashCodeMethod { get; private set; }
public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
{
if (equals == null) throw new ArgumentNullException("equals", "Equals parameter is required for all InlineComparer instances");
EqualsMethod = equals;
GetHashCodeMethod = hashCode;
}
public bool Equals(T x, T y)
{
return EqualsMethod(x, y);
}
public int GetHashCode(T obj)
{
if (GetHashCodeMethod == null) return obj.GetHashCode();
return GetHashCodeMethod(obj);
}
}
Usage sample:
var comparer = new InlineComparer<DetalleLog>((i1, i2) => i1.PeticionEV == i2.PeticionEV && i1.Etiqueta == i2.Etiqueta, i => i.PeticionEV.GetHashCode() + i.Etiqueta.GetHashCode());
var peticionesEV = listaLogs.Distinct(comparer).ToList();
Assert.IsNotNull(peticionesEV);
Assert.AreNotEqual(0, peticionesEV.Count);
Source:
https://stackoverflow.com/a/5969691/206730
Using IEqualityComparer for Union
Can I specify my explicit type comparator inline?
If Distinct() doesn't produce unique results, try this one:
var filteredWC = tblWorkCenter.GroupBy(cc => cc.WCID_I).Select(grp => grp.First()).Select(cc => new Model.WorkCenter { WCID = cc.WCID_I }).OrderBy(cc => cc.WCID);
ObservableCollection<Model.WorkCenter> WorkCenter = new ObservableCollection<Model.WorkCenter>(filteredWC);
A tricky way to do this is use Aggregate() extension, using a dictionary as accumulator with the key-property values as keys:
var customers = new List<Customer>();
var distincts = customers.Aggregate(new Dictionary<int, Customer>(),
(d, e) => { d[e.CustomerId] = e; return d; },
d => d.Values);
And a GroupBy-style solution is using ToLookup():
var distincts = customers.ToLookup(c => c.CustomerId).Select(g => g.First());
IEnumerable lambda extension:
public static class ListExtensions
{
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, int> hashCode)
{
Dictionary<int, T> hashCodeDic = new Dictionary<int, T>();
list.ToList().ForEach(t =>
{
var key = hashCode(t);
if (!hashCodeDic.ContainsKey(key))
hashCodeDic.Add(key, t);
});
return hashCodeDic.Select(kvp => kvp.Value);
}
}
Usage:
class Employee
{
public string Name { get; set; }
public int EmployeeID { get; set; }
}
//Add 5 employees to List
List<Employee> lst = new List<Employee>();
Employee e = new Employee { Name = "Shantanu", EmployeeID = 123456 };
lst.Add(e);
lst.Add(e);
Employee e1 = new Employee { Name = "Adam Warren", EmployeeID = 823456 };
lst.Add(e1);
//Add a space in the Name
Employee e2 = new Employee { Name = "Adam Warren", EmployeeID = 823456 };
lst.Add(e2);
//Name is different case
Employee e3 = new Employee { Name = "adam warren", EmployeeID = 823456 };
lst.Add(e3);
//Distinct (without IEqalityComparer<T>) - Returns 4 employees
var lstDistinct1 = lst.Distinct();
//Lambda Extension - Return 2 employees
var lstDistinct = lst.Distinct(employee => employee.EmployeeID.GetHashCode() ^ employee.Name.ToUpper().Replace(" ", "").GetHashCode());
The Microsoft System.Interactive package has a version of Distinct that takes a key selector lambda. This is effectively the same as Jon Skeet's solution, but it may be helpful for people to know, and to check out the rest of the library.
Here's how you can do it:
public static class Extensions
{
public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query,
Func<T, V> f,
Func<IGrouping<V,T>,T> h=null)
{
if (h==null) h=(x => x.First());
return query.GroupBy(f).Select(h);
}
}
This method allows you to use it by specifying one parameter like .MyDistinct(d => d.Name), but it also allows you to specify a having condition as a second parameter like so:
var myQuery = (from x in _myObject select x).MyDistinct(d => d.Name,
x => x.FirstOrDefault(y=>y.Name.Contains("1") || y.Name.Contains("2"))
);
N.B. This would also allow you to specify other functions like for example .LastOrDefault(...) as well.
If you want to expose just the condition, you can have it even simpler by implementing it as:
public static IEnumerable<T> MyDistinct2<T, V>(this IEnumerable<T> query,
Func<T, V> f,
Func<T,bool> h=null
)
{
if (h == null) h = (y => true);
return query.GroupBy(f).Select(x=>x.FirstOrDefault(h));
}
In this case, the query would just look like:
var myQuery2 = (from x in _myObject select x).MyDistinct2(d => d.Name,
y => y.Name.Contains("1") || y.Name.Contains("2")
);
N.B. Here, the expression is simpler, but note .MyDistinct2 uses .FirstOrDefault(...) implicitly.
Note: The examples above are using the following demo class
class MyObject
{
public string Name;
public string Code;
}
private MyObject[] _myObject = {
new MyObject() { Name = "Test1", Code = "T"},
new MyObject() { Name = "Test2", Code = "Q"},
new MyObject() { Name = "Test2", Code = "T"},
new MyObject() { Name = "Test5", Code = "Q"}
};
I'm assuming you have an IEnumerable<T>, and in your example delegate, you would like c1 and c2 to be referring to two elements in this list?
I believe you could achieve this with a self join:
var distinctResults = from c1 in myList
join c2 in myList on <your equality conditions>
I found this as the easiest solution.
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
return source.GroupBy(keySelector).Select(x => x.FirstOrDefault());
}

IEnumerable.Except() and a custom comparer

I'm having troubles with the Except() method.
Instead of returning the difference, it returns the original set.
I've tried implementing the IEquatable and IEqualityComparer in the Account class.
I've also tried creating a separate IEqualityComparer class for Account.
When the Except() method is called from main, it doesn't seem to call my custom Equals() method, but when I tried Count(), it did call the custom GetHashCode() method!
I'm sure I made a trivial mistake somewhere and I hope a fresh pair of eyes can help me.
main:
IEnumerable<Account> everyPartnerID =
from partner in dataContext.Partners
select new Account { IDPartner = partner.ID, Name = partner.Name };
IEnumerable<Account> hasAccountPartnerID =
from partner in dataContext.Partners
from account in dataContext.Accounts
where
!partner.ID.Equals(Guid.Empty) &&
account.IDPartner.Equals(partner.ID) &&
account.Username.Equals("Special")
select new Account { IDPartner = partner.ID, Name = partner.Name };
IEnumerable<Account> noAccountPartnerID =
everyPartnerID.Except(
hasAccountPartnerID,
new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)));
Account:
public class Account : IEquatable<Account>
{
public Guid IDPartner{ get; set; }
public string Name{ get; set; }
/* #region IEquatable<Account> Members
public bool Equals(Account other)
{
return this.IDPartner.Equals(other.IDPartner);
}
#endregion*/
}
LambdaComparer:
public class LambdaComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _lambdaComparer;
private readonly Func<T, int> _lambdaHash;
public LambdaComparer(Func<T, T, bool> lambdaComparer) :
this(lambdaComparer, o => o.GetHashCode())
{
}
public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
{
if (lambdaComparer == null)
throw new ArgumentNullException("lambdaComparer");
if (lambdaHash == null)
throw new ArgumentNullException("lambdaHash");
_lambdaComparer = lambdaComparer;
_lambdaHash = lambdaHash;
}
public bool Equals(T x, T y)
{
return _lambdaComparer(x, y);
}
public int GetHashCode(T obj)
{
return _lambdaHash(obj);
}
}
Basically your LambdaComparer class is broken when you pass in just a single function, because it uses the "identity hash code" provider if you don't provide anything else. The hash code is used by Except, and that's what's causing the problem.
Three options here:
Implement your own ExceptBy method and then preferably contribute it to MoreLINQ which contains that sort of thing.
Use a different implementation of IEqualityComparer<T>. I have a ProjectionEqualityComparer class you can use in MiscUtil - or you can use the code as posted in another question.
Pass a lambda expression into your LambdaComparer code to use for the hash:
new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)),
x => x.IDPartner.GetHashCode());
You could also quickly fix your LambdaComparer to work when only the equality parameters are supplied like this:
public LambdaComparer(Func<T, T, bool> lambdaComparer) :
this(lambdaComparer, o => 1)
{
}
Look here, how to use and implementing IEqualityComparer in way with linq.Except and beyond.
https://www.dreamincode.net/forums/topic/352582-linq-by-example-3-methods-using-iequalitycomparer/
public class Department {
public string Code { get; set; }
public string Name { get; set; }
}
public class DepartmentComparer : IEqualityComparer {
// equal if their Codes are equal
public bool Equals(Department x, Department y) {
// reference the same objects?
if (Object.ReferenceEquals(x, y)) return true;
// is either null?
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Code == y.Code;
}
public int GetHashCode(Department dept) {
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
// if null default to 0
if (Object.ReferenceEquals(dept, null)) return 0;
return dept.Code.GetHashCode();
}
}
IEnumerable<Department> deptExcept = departments.Except(departments2,
new DepartmentComparer());
foreach (Department dept in deptExcept) {
Console.WriteLine("{0} {1}", dept.Code, dept.Name);
}
// departments not in departments2: AC, Accounts.
IMO, this answer above is the simplest solution compared to other solutions for this problem. I tweaked it such that I use the same logic for the Object class's Equals() and GetHasCode(). The benefit is that this solution is completely transparent to the client linq expression.
public class Ericsson4GCell
{
public string CellName { get; set; }
public string OtherDependantProperty { get; set; }
public override bool Equals(Object y)
{
var rhsCell = y as Ericsson4GCell;
// reference the same objects?
if (Object.ReferenceEquals(this, rhsCell)) return true;
// is either null?
if (Object.ReferenceEquals(this, null) || Object.ReferenceEquals(rhsCell, null))
return false;
return this.CellName == rhsCell.CellName;
}
public override int GetHashCode()
{
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
// if null default to 0
if (Object.ReferenceEquals(this, null)) return 0;
return this.CellName.GetHashCode();
}
}

Linq Distinct() by name for populate a dropdown list with name and value

I'm trying to populate a Drop down list with pharmaceutical companies, like Bayer, Medley etc. And, I'm getting theses names from DB and theses names are repeated in DB, but with different id's.
I'm trying to use Linq Distinct(), but I don't want to use the equality comparer. Is there another way?
My drop down list must be filled with the id and the name of the company.
I'm trying something like:
var x = _partnerService
.SelectPartners()
.Select(c => new {codPartner = c.codPartner, name = c.name})
.Distinct();
This is showing repeated companies in ddl.
thanks!
The following expression will select only distinct companies and return the first occurence with its id.
partnerService.SelectPartners().GroupBy(p => p.Name).Select(g => g.First());
var distinctCompanies = Companies
.GroupBy(c => c.CompanyName)
.Select(g => g.First());
Distinct works on the entire select. If you include c.codPartner in the select, and there are two different values of c.codPartner for the same c.name, then you are going to see two rows with the same c.name.
I don't think you can do this with an anonymous class, but if you created a data object like
class Foo
{
private int _ID;
public int ID
{
get { return _ID; }
set { _ID = value; }
}
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
you could create a comparer object like
class FooComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
return x.Name == y.Name;
}
public int GetHashCode(Foo obj)
{
return obj.GetHashCode();
}
}
If you do not specify a IEqualityComparer parameter, then it will just use Object.ReferenceEquals, which looks at the objects GetHashKey value. For anonymous types, they are unique.
Now solving this is a bit tricky, since you cannot write an IEqualityComparer for an anonymous type. So you muct create a real type for the problem:
class Partner
{
public int codPartner {get; set;}
public string name {get; set;}
public override int GetHashCode() { return name .GetHashCode();}
}
var x = _partnerService.SelectPartners()
.Select(c => new Partner {codPartner = c.codPartner, name = c.name})
.Distinct();
Distinc will use GetHashCode if you donĀ“t tell it (via an IEqualityComparer) to use another method.
You could use a generic equalitycomparer, like this:
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, T, Boolean> comparer;
public GenericEqualityComparer(Func<T, T, Boolean> comparer)
{
this.comparer = comparer;
}
#region IEqualityComparer<T> Implementation
public bool Equals(T x, T y)
{
return comparer(x, y);
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
#endregion
}
and then use like this (kindof)
public static IEqualityComparer<YourType> MyComparer
{
get
{
return new GenericEqualityComparer<YourType>((x, y) =>
{
return x.name.Equals(y.name);
});
}
}
just pass in your own comparer to the Distinct method using one of the other overloads.
(extension) IQueryable<T> IQueryable<T>.Distinct( IEqualityComparer<T> comparer )

Categories

Resources