Create List<CustomObject> from List<OtherObject>, removing duplicates.
There's a very related question: Create List<CustomObject> from List<string> however it doesn't deal with removing duplicates at the same time.
I have the following class examples:
class Widget
{
public string OwnerName;
public int SomeValue;
}
class Owner
{
public string Name;
public string OtherData;
}
I'd like to create a list of owners based on a list of Widgets, but only unique owner names.
H开发者_Go百科ere is what I tried:
List<Owner> Owners = MyWidgetList.Select(w => new Owner { Name = w.OwnerName }).Distinct().ToList();
The problem is that there are repeats in the resulting list. What am I doing wrong?
You need to define GetHashCode()
and Equals()
for your object to define what equality is for your custom type. Otherwise it compares based on the reference itself.
This is because the LINQ extension methods use the IEqualityComparer
interface to compare objects. If you don't define a custom comparer (which you can do by creating a separate class that implements IEqualityComparer<Owner>
) it will use the default equality comparer, which uses the class's Equals()
and GetHashCode()
definition. Which, if you don't override them, does reference comparisons on Equals()
and returns the default object hash code.
Either define a custom IEqualityComparer<Owner>
(since you're calling distinct on a sequence of Owner) or add a Equals()
and GetHashCode()
for your class.
public class Owner
{
public string Name;
public string OtherData;
public override Equals(object other)
{
if (ReferenceEquals(this, other))
return true;
if (other == null)
return false;
// whatever your definition of equality is...
return Name == other.Name && OtherData == other.OtherData;
}
public override int GetHashCode()
{
int hashCode = 0;
unchecked
{
// whatever hash code computation you want, but for example...
hashCode += 13 * Name != null ? Name.GetHashCode() : 0;
hashCode += 13 * OtherData != null ? OtherData.GetHashCode() : 0;
}
return hashCode;
}
}
Once you do that, the query you have written will work just fine.
The default comparer for Owner
isn't working correctly (since it's just using reference equality) so Distinct
thinks all the objects are different. One solution is to use two Select
s:
var owners = MyWidgetList.Select(w => w.OwnerName).Distinct().Select(w => new Owner { Name = w }).ToList();
Alternatively, you could just implement Equals
and GetHashCode
on Owner
, and your original method would work.
You owner object must implement IComparable<>
for Distinct
to work correctly. You will have to decide what makes an Owner
the same as another Owner
.
Distinct uses equals to determine whether two element are the same. You could implement equals for your owner. Or select the name first like this:
List<Owner> Owners = MyWidgetList.Select(w => w.OwnerName).Distinct()
.Select(s=>new Owner{Name = s}.ToList();
I guess this Distinct tries to get unique object from the Owner
class you instantiated, why not just select the string
try this:
List<string> OwnersStr = MyWidgetList.Select(w => w.OwnerName).Distinct().ToList();
then create a list from them
精彩评论