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;
}
Related
I have a list of long type array.
List<ulong[]> TestList = new List<ulong[]>();
and list has following items.
{1,2,3,4,5,6},
{2,3,4,5,6,7},
{3,4,5,6,7,8},
{1,2,3,4,5,6}
and expected distinct result is
{1,2,3,4,5,6},
{2,3,4,5,6,7},
{3,4,5,6,7,8}
So I try as following, but useless.
TestList = TestList.Distinct().ToList();
Am I need something special comparer for getting distinct list?
Distinct() uses the default equality check, which for arrays is reference equality. It does not check the contents of the array for equality.
If you want to do that, you'll need the overload of Distinct() that takes an IEqualityComparer<T>. This allows you to customize the behaviour to determine if two items are equal or not.
For comparing arrays, IStructuralEquatable and friends already do the heavy lifting. You can wrap it simply, like so:
sealed class StructuralComparer<T> : IEqualityComparer<T>
{
public static IEqualityComparer<T> Instance { get; } = new StructuralComparer<T>();
public bool Equals(T x, T y)
=> StructuralComparisons.StructuralEqualityComparer.Equals(x, y);
public int GetHashCode(T obj)
=> StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
}
Then, use it in the Distinct() call like this:
TestList = TestList.Distinct(StructuralComparer<ulong[]>.Instance).ToList();
You need to provide an equality comparer, default implementation does not know how to compare arrays of long (it uses reference equality):
class LongArrayComparer : EqualityComparer<long[]>
{
public override bool Equals(long[] a1, long[] a2)
{
if (a1 == null && a2 == null)
return true;
else if (a1 == null || a2 == null)
return false;
return a1.SequenceEqual(a2);
}
public override int GetHashCode(long[] arr)
{
long hCode = arr.Aggregate(0, (acc, it) => acc ^ it);
return hCode.GetHashCode();
}
}
Then use it:
TestList = TestList.Distinct(new LongArrayComparer()).ToList();
List<ulong[]> TestList = new List<ulong[]>() {
new ulong[]{ 1,2,3,4,5,6},
new ulong[]{ 2,3,4,5,6,7},
new ulong[]{ 3,4,5,6,7,8},
new ulong[]{ 1,2,3,4,5,6}
};
var result = TestList.GroupBy(x => String.Join(",", x))
.Select(x => x.First().ToArray())
.ToList();
You can implement an IEqualityComparer
public class IntArrayComparer : IEqualityComparer<string[]>
{
public bool Equals(int[] x, int[] y)
{
var shared = x.Intersect(y);
return x.Length == y.Length && shared.Count() == x.Length;;
}
public int GetHashCode(int[] obj)
{
int hashCode=obj.Length;
for(int i=0;i<obj.Length;++i)
{
hashCode=unchecked(hashCode*314159 +obj[i]);
}
return hashCode;
}
}
Then can implement it:
TestList = TestList.Distinct(new IntArrayComparer()).ToList();
I have a list as following:
public class MyClass
{
public int val1;
public long val2;
public string val3;
}
class Program
{
static void Main(string[] args)
{
List<MyClass> lstData=new List<MyClass>
{
new MyClass{val1=1,val2=2,val3="AA"},
new MyClass{val1=1,val2=2,val3="BB"},
new MyClass{val1=3,val2=4,val3="AA"},
new MyClass{val1=3,val2=4,val3="BB"},
new MyClass{val1=1,val2=2,val3="BB"},
new MyClass{val1=3,val2=4,val3="AA"},
};
}
}
I want to get unique rows out of the list.In the above example following lists are duplicate
{val1=1,val2=2,val3="BB"}
{val1=3,val2=4,val3="AA"}
I want to eliminate the duplicate rows and get the distinct rows out of the list.
How I can do that?
var result = lstData.GroupBy(x=>new {x.val1, x.val2, x.val3}, (x,y)=>y.First())
.ToList();
Assuming that your real class MyClass overrides Equals and GetHashCode, all you need is to add Distinct:
lstData = lstData.Distinct().ToList();
You have to either implement GetHashCode and Equals methods on your MyClass class, or define custom class which implements IEqualityComparer<MyClass>.
public class MyClassEqualityComparer : IEqualityComparer<MyClass>
{
private static Lazy<MyClassEqualityComparer> _instance = new Lazy<MyClassEqualityComparer>(() => new MyClassEqualityComparer());
public static MyClassEqualityComparer Instance
{
get { return _instance.Value; }
}
private MyClassEqualityComparer() { }
public bool Equals(MyClass x, MyClass y)
{
return x.val1 == y.val1 && x.val2 == y.val2 && x.val3 == y.val3;
}
public int GetHashCode(MyClass obj)
{
return obj.val1.GetHashCode() ^ obj.val2.GetHashCode() ^ obj.val3.GetHashCode();
}
}
With that you'll be able to use LINQ Distinct() method:
var distinct = lstData.Distinct(MyClassEqualityComparer.Instance).ToList();
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);
I have a long line with an OrderBy inside. This line repeats itself several times with only a minor difference:
OrderBy(m => m.Name)
OrderBy(m => m.CreationTime)
etc.
In the interest of maintainability, is there a way to make only one line, and have it call some method, or perhaps use some Action that will take care of returning the correct field? The problem is that the fields are not of the same type.
Look at this method signature:
public static IOrderedEnumerable OrderBy(
this IEnumerable source,
Func keySelector,
IComparer comparer
)
You need to give it IComparer<TKey>
data.OrderBy(x => x, new FooComparer());
data.OrderBy(x => x, new BarComparer());
data.OrderBy(x => x, new BazComparer());
Not sure if it's much cleaner to create classes just for sorting...
You can also create different methods that sort the list according to an enum they get as a parameter,
I think it's a lot cleaner solution.
MSDN
Maybe this comes closer to what you want
public IComparable GetKey(int keyNum)
{
switch (keyNum) {
case 1:
return Name;
case 2:
return CreationTime;
default:
return null;
}
}
Sorting would occur like this
.OrderBy(m => m.GetKey(1))
Yet another way is to return a delegate
public static Func<MyClass, IComparable> GetKeySelector(int keyNum)
{
switch (keyNum) {
case 1:
return m => m.Name;
case 2:
return m => m.CreationTime;
default:
throw new ArgumentException();
}
}
.OrderBy(MyClass.GetKeySelector(1));
It sounds like you are copying the same entire selection multiple times? You would just need this:
.OrderBy(m=>m.Name)
.ThenBy(m=>m.CreationTime);
After OrderBy you must use ThenBy
.OrderBy(m => m.Name)
.ThenBy(m => m.CreationTime)
But this does not answer your question. Implement IComparable<Type_of_m> in the type of m, then you can compare with
.OrderBy(m => m)
class Type_of_m : IComparable<Type_of_m>
{
public string Name { get; set; }
public DateTime CreationTime { get; set; }
public int CompareTo(Type_of_m other)
{
int comp = Name.CompareTo(other.Name);
if (comp != 0) {
return comp;
}
return CreationTime.CompareTo(other.CreationTime);
}
}
If you want to be able to order the items in several ways, then use a Comparer as gdoron
explained. This is a good case for nested classes.
class MyClass
{
public string Name { get; set; }
public DateTime CreationTime { get; set; }
public static readonly IComparer<MyClass> NameComparer = new MyNameComparaer();
private class MyNameComparaer : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
return x.Name.CompareTo(y.Name);
}
}
public static readonly IComparer<MyClass> DateComparer = new MyDateComparer();
private class MyDateComparer : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
return x.CreationTime.CompareTo(y.CreationTime);
}
}
}
You can then order like this
.OrderBy(m => m, MyClass.NameComparer)
or like this
.OrderBy(m => m, MyClass.DateComparer)
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 )