Reputation: 244
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.
Upvotes: 4
Views: 20406
Reputation: 550
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; }
}
Upvotes: 1
Reputation: 113
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;
}
Upvotes: 0
Reputation: 61912
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.
Upvotes: 0
Reputation: 26291
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();
Upvotes: 0
Reputation: 70718
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();
}
Upvotes: 8