通过过滤将List <>添加到另一个List <>

时间:2019-12-11 08:51:52

标签: c# .net list

我有一个模型课:

public class Person 
{
    public int Id { get; set; }
    public string Name { get; set; }
}

当我添加两个列表时:

List<Person> people1 = new List<Person> {
    new Person() { Id = 1, Name = "Name1" },
    new Person() { Id = 2, Name = "Name2" },
    new Person() { Id = 3, Name = "Name3" },
};

List<Person> people2 = new List<Person> {
    new Person() { Id = 1, Name = "Name1" },
    new Person() { Id = 4, Name = "Name4" },
    new Person() { Id = 5, Name = "Name5" },
};

people1.AddRange(people2);

如果person中的people2id中的person中具有相同的people1,则我不想添加它。我该怎么办?

5 个答案:

答案 0 :(得分:10)

可以对此使用LINQ相当容易但效率低下:

people1.AddRange(people2.Where(p2 => !people1.Any(p1 => p1.Id == p2.Id)));

或者您可以先创建一组ID:

HashSet<int> people1Ids = new HashSet<int>(people1.Select(p1 => p1.Id));
people1.AddRange(people2.Where(p2 => !people1Ids.Contains(p2.id));

第一种方法显然更简单,但是如果列表很大,它可能会变慢,因为对于people2中的 every 元素,它将遍历 every < / em> people1中的元素。

如果people1很大,第二种方法将大大加快 。如果people2很大,您将不会获得太大收益。例如,如果people1仅包含几个人,则在哈希集中查找ID不会比遍历列表快得多。

您可以采取完全不同的方法。如果您根据ID使Person类型的实现IEquatable<Person>-或创建一个这样做的IEqualityComparer<Person>,并且 if 则不需要那么多现有的要修改的列表,只要您需要“两个列表的并集”,而 if 则不必关心顺序,而 if 中的所有条目每个列表都是唯一的,或者您不介意删除重复列表,您可以使用:

// Specify the comparer as another argument if you need to.
// You could call ToList on the result if you need a list.
var union = people1.Union(people2);

(该解决方案有很多条件,但可能都有效)。

答案 1 :(得分:3)

您可以将Union运算符与自定义IEqualityComparer配合使用。这将创建一个新列表,该列表是其他2的组合。实现客户IEqualityComparer可让您控制组成同一记录的内容。

var allPeople = people1.Union(people2, new PersonComparer());

public class PersonComparer : IEqualityComparer<Person>
{
  public bool Equals(Person x, Person y)
  {
    // ommited null checks etc
    return x.Id == y.Id;
  }

  public int GetHashCode(Person obj)
  {
    // ommited null checks etc
    obj.Id.GetHashCode()
  }
}

答案 2 :(得分:2)

使用此:

people1.AddRange(people2.Except(people1));

但是您首先需要在Person类中重写Equal和GetHashCode:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        if (!(obj is Person))
            return false;
        Person p = (Person)obj;
        return (p.Id == Id && p.Name == Name);
    }
    public override int GetHashCode()
    {
        return String.Format("{0}|{1}", Id, Name).GetHashCode();
    }
}

或者您可以使用自定义的相等比较器,然后使用Distinct(new DistinctItemComparer())

public class DistinctItemComparer : IEqualityComparer<Person>
{

    public bool Equals(Person x, Person y)
    {
        return x.Id == y.Id &&
            x.Name == y.Name;
    }

    public int GetHashCode(Person obj)
    {
        return obj.Id.GetHashCode() ^
            obj.Name.GetHashCode();
    }
}

然后像这样使用它:

people1.AddRange(people2.Except(people1, new DistinctItemComparer()));

如果您只需要基于Id进行区分,则可以从这两种方法中排除Name

基于this,第二种方法似乎更好,因为Microsoft已经建议不要重载引用类型上的运算符等于

答案 3 :(得分:1)

我认为这里的最佳解决方案是使用LINQ。它写的代码非常简单和简短:

people1.AddRange(people2.Where(x => !people1.Any(y => x.Id == y.Id)));

答案 4 :(得分:1)

您是否考虑过改用Dictionary

您可以将Id用作Key,它不允许重复的密钥存在吗?

以下代码是我以前使用的方式:

var dictionary = people1.ToDictionary(x => x.Id, x => x);

foreach(var person in people2)
{
    if(!dictionary.ContainsKey(item.Id))
    {
        dictionary.Add(item.Id, item);
    }
}

也许有更好的方法,但这对我有用。

这样,当您向字典中添加项目时,就不会再添加具有相同Id的内容。

也检查HashSets,因为他们做类似的事情。