如何比较两个共享的 属性 上的两个 HashSet?

How do I compare two HashSets on one property that both share?

我需要比较两个类型相同但仅某些属性具有不同值的哈希集。我本质上需要一个更具体的 ExceptWith.

我试过使用 ExceptWith,但据我所知,这不允许您指定要比较的 属性。

我们应该假装我不能向 Person 添加或删除任何属性 class。

   class Program
{
    private class Person
    {
        public string Name { get; set; }
        public string Id { get; set; }
    }

    static void Main(string[] args)
    {
        var people1 = new[]
        {
            new Person
            {
                Name = "Amos",
                Id = "123"
            },
            new Person
            {
                Name = "Brian",
                Id = "234"
            },
            new Person
            {
                Name = "Chris",
                Id = "345"
            },
            new Person
            {
                Name = "Dan",
                Id = "456"
            }
        };

        var people2 = new[]
        {
            new Person
            {
                Name = "Amos",
                Id = "098"
            },
            new Person
            {
                Name = "Dan",
                Id = "987"
            }
        };

        var hash1 = new HashSet<Person>(people1);

        var hash2 = new HashSet<Person>(people2);

        var hash3 = new HashSet<Person>(); // where hash3 is hash1 without the objects from hash2 solely based on matching names, not caring about Id matching

        foreach (var item in hash3) // should print out Brian, Chris
        {
            Console.WriteLine($"{item.Name}");
        }
    }
}

在您的 Person class 中,您应该定义自己的 GetHashCode 方法,这样它只使用人名而不使用 ID。

如果这样做,您还必须定义自己的 Equals 方法:Why is it important to override GetHashCode when Equals method is overridden?

你可以用 linq 做到这一点:

var hash3 = hash1.Where(x=>!hash2.Select(y=>y.Name).Contains(x.Name)).ToHashSet();

这将创建一个仅包含来自 hash2 的姓名的集合,然后从 hash1 中获取姓名不在该集合中的所有人物。

您可以对第二个数组中的名称进行哈希处理,以在 Linq 过滤器中使用以创建最终的 HashSet

var excludeName = new HashSet<string>(people2.Select(x => x.Name));
var hash3 = new HasSet<Person>(people1.Where(x => !exludeName.Contains(x.Name));

如果要排除的值列表非常大,这将特别有用,因为它会使整个过程在线性时间内 运行。

或者您可以通过以下方式设置 HashSetIEqualityComparer<T>

public class PersonByNameComparer : IEqualityComparer<Peron>
{
    public bool Equals(Person p1, Persion p2)
    {
        return p1.Name == p2.Name;
    }

    public int GetHashCode(Person p)
    {
        return p.Name.GetHashCode();
    }
}

注意:这意味着 HashSet 不能包含两个具有相同 Name 的项目,即使 Id 不同。但这也意味着它不能像您当前的设置那样包含具有相同值的不同对象。

然后就这样使用了

var comparer = new PersonByNameComparer();

var hash1 = new HashSet<Person>(people1, comparer);
var hash2 = new HashSet<Person>(people2, comparer);

// Note that ExceptWith will mutate the existing hash.  
hash1.ExceptWith(hash2); 

// Alternatively you can create the new hash like this
var hash3 = new HashSet<Persion>(hash1.Where(p => !hash2.Contains(p)));