【问题标题】:Remove duplicates from array of objects从对象数组中删除重复项
【发布时间】:2016-03-12 13:56:09
【问题描述】:

我有一个名为Customer 的类,它有几个字符串属性,例如

firstName, lastName, email, etc.  

我从创建类数组的csv 文件中读取客户信息:

Customer[] customers  

我需要删除具有相同电子邮件地址的重复客户,每个特定电子邮件地址只保留 1 条客户记录。

我已使用 2 个循环完成此操作,但需要将近 5 分钟,因为通常有 50,000 多个客户记录。完成删除重复项后,我需要将客户信息写入另一个 csv 文件(此处无需帮助)。

如果我在循环中执行Distinct,我将如何删除属于该特定客户的类的其他字符串变量?

谢谢, 安德鲁

【问题讨论】:

  • 这个想法是每天/每周/每季度运行一次吗?此任务的频率可能会决定解决方案的持久性。
  • 如果不使用新的相等比较器,Distinct 将不适用于自定义类型。使用 MoreLinq 中的 DistinctBy。顺便说一句,对于 50k 个项目,此操作不会花费太多时间,因为 distinct 是 O(n)
  • 我的选择可能是通过重复键(在您的情况下为电子邮件)对输入文件进行排序,并在添加到您的对象之前对当前值进行简单的比较。
  • 我会使用KeyedCollection(在System.Collectons.ObjectModell)。让电子邮件成为 Key 并在检查 Contains 后插入。这非常快......
  • 可能相关/有帮助:stackoverflow.com/questions/2537823/…

标签: c# arrays class duplicates


【解决方案1】:

使用 Linq,您可以使用 GroupBy 在 O(n) 时间(单级循环)内完成此操作

var uniquePersons = persons.GroupBy(p => p.Email)
                           .Select(grp => grp.First())
                           .ToArray();

更新

关于O(n)GroupBy 的行为。

GroupBy 是在 Linq (Enumerable.cs) 中实现的 -

IEnumerable 仅迭代一次以创建分组。提供的键的Hash(例如这里的“电子邮件”)用于查找唯一键,并在与键对应的Grouping中添加元素。

请查看此GetGrouping 代码。还有一些旧帖供参考。

那么Select 显然是一个O(n) 代码,使得上面的代码整体为O(n)

更新 2

处理empty/null 值。

因此,如果存在Email 的值为nullempty 的实例,则简单的GroupBy 将仅从nullempty 中获取其中一个对象。

使用null/empty 值包含所有这些对象的一种快速方法是在运行时为这些对象使用一些唯一键,例如

var tempEmailIndex = 0;
var uniqueNullAndEmpty = persons
                         .GroupBy(p => string.IsNullOrEmpty(p.Email) 
                                       ? (++tempEmailIndex).ToString() : p.Email)
                         .Select(grp => grp.First())
                         .ToArray();

【讨论】:

  • “由于 Linq 正在使用反射” - 你有这方面的参考吗?
  • @Shnugo - LINQ 不使用反射。
  • 你能告诉我们你是怎么知道这将在 O(n) 中执行的吗?
  • @MikeNakis 请看我已经用解释和参考更新了答案。
  • @AWooster,说“谢谢”非常好,但请看这里:someone-answers(顺便说一句:我这边 +1)
【解决方案2】:

我会这样做:

public class Person {
    public Person(string eMail, string Name) {
        this.eMail = eMail;
        this.Name = Name;
    }
    public string eMail { get; set; }
    public string Name { get; set; }
}
public class eMailKeyedCollection : System.Collections.ObjectModel.KeyedCollection<string, Person> {
    protected override string GetKeyForItem(Person item) {
        return item.eMail;
    }
}

public void testIt() {
    var testArr = new Person[5];
    testArr[0] = new Person("Jon@Mullen.com", "Jon Mullen");
    testArr[1] = new Person("Jane@Cullen.com", "Jane Cullen");
    testArr[2] = new Person("Jon@Cullen.com", "Jon Cullen");
    testArr[3] = new Person("John@Mullen.com", "John Mullen");
    testArr[4] = new Person("Jon@Mullen.com", "Test Other"); //same eMail as index 0...

    var targetList = new eMailKeyedCollection();
    foreach (var p in testArr) {
        if (!targetList.Contains(p.eMail))
            targetList.Add(p);
    }
}

如果在集合中找到该项目,您可以轻松地选择(并最终修改)它:

        if (!targetList.Contains(p.eMail))
            targetList.Add(p);
        else {
           var currentPerson=targetList[p.eMail];
           //modify Name, Address whatever... 
        }

【讨论】:

    猜你喜欢
    • 2017-04-10
    • 2019-02-28
    • 1970-01-01
    • 1970-01-01
    • 2019-05-01
    相关资源
    最近更新 更多