从List<OtherObject>创建List<CustomObject>,并删除重复项。

3

有一个相关的问题:从List<string>创建List<CustomObject>,但它没有同时处理重复项。

我有以下类举例:

class Widget
{
    public string OwnerName;
    public int SomeValue;
}

class Owner
{
    public string Name;
    public string OtherData;
}

我将根据 Widget 列表创建一个所有者列表,但只包含唯一的所有者名称。以下是我尝试过的方法:
List<Owner> Owners = MyWidgetList.Select(w => new Owner { Name = w.OwnerName }).Distinct().ToList();

问题在于结果列表中有重复项。我做错了什么?

这就是为什么我们喜欢SO的原因,对吧? :) - JYelton
@Gluip:那不是尼克·凯奇的电影吗? - James Michael Hare
5个回答

5

Owner 的默认比较器并没有正确地工作(因为它仅使用引用相等性),所以 Distinct 认为所有对象都是不同的。一个解决方法是使用两个 Select

var owners = MyWidgetList.Select(w => w.OwnerName).Distinct().Select(w => new Owner { Name = w }).ToList();

或者你可以在 Owner 上实现 EqualsGetHashCode,这样你原来的方法就能工作。


这意味着需要记住在每个仅需要不同值的查询中执行此操作。实现IComparable更加整洁。 - jaywayco
@jay 我认为 Equals()GetHashCode() 应该可以解决问题,因为在内部 Distinct 会通过元素的哈希码尝试将其添加到集合中,并在已存在时跳过它。话虽如此,IComparable 也是一个不错的选择。 - dlev
+1 感谢您展示了如何使用LINQ完成此操作,这对我正在处理的特定情况非常合适。 - JYelton

5
您需要为您的对象定义GetHashCode()Equals(),以定义自定义类型的相等性。否则,它将基于引用本身进行比较。
这是因为LINQ扩展方法使用IEqualityComparer接口来比较对象。如果您不定义自定义比较器(可以通过创建实现IEqualityComparer<Owner>的单独类来完成),它将使用默认的相等性比较器,该比较器使用类的Equals()GetHashCode()定义。如果您没有覆盖它们,则在Equals()上执行引用比较并返回默认对象哈希代码。
要么定义一个自定义的IEqualityComparer<Owner>(因为您正在对Owner序列调用distinct),要么为您的类添加Equals()GetHashCode()
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;
    }
}

一旦您完成此操作,您编写的查询将正常运行。

我看到我们的思维方式很相似!点赞因为你实际上展示了如何实现这些方法。 - dlev
关于“正确答案”的选择有些棘手-@dlev展示了使用LINQ的解决方法,这非常好,因为这仅是填充treeview的一次性操作(我在此情况下的选择)。然而,James提供了一个非常好的例子,说明如何进行更加“弹无虚发”的操作,并且如果我对这个类进行更多操作,这个例子就会适用。 - JYelton
@JYelton:别担心,只需选择最适合你的解决方案并点赞另一个,我们都会很开心。 - James Michael Hare
对于当前的项目,我使用了@dlev的LINQ方法,但我相信实现Equals/GetHashCode是大多数情况下的首选方式。 - JYelton

1

您的对象必须实现 IComparable<> 接口,以使 Distinct 方法正常工作。您需要决定什么样的属性会使一个 Owner 对象与另一个 Owner 对象相同。


1

Distinct使用equals来确定两个元素是否相同。您可以为自己实现equals,或者像这样首先选择名称:

List<Owner> Owners = MyWidgetList.Select(w => w.OwnerName).Distinct()
 .Select(s=>new Owner{Name = s}.ToList();

你的假设名称是Owner对象的唯一标识符。 - jaywayco

0
我猜这里的 Distinct 尝试从你实例化的 Owner 类中获取唯一的对象,为什么不直接选择字符串呢?
尝试这样做:
List<string> OwnersStr = MyWidgetList.Select(w =>  w.OwnerName).Distinct().ToList();

然后从它们创建一个列表


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接