2013-03-27 242 views
0

假设我们有一个“A学生”列表和一个“B学生”列表。然后,我们将这两个列表添加到更通用的列表中,称为“学生”。然后有人决定通过在通用“学生”列表中添加一个“A学生”的重复列表来使我们的生活复杂化。删除“学生”重复列表中最有效的方法是什么?请注意,涉及两个自定义类。C#从包含列表的列表中删除重复项

代码中的通用学生列表称为lstStudents。这是我想删除任何重复的列表。

(我试图拿出一个更好的例子,但是这是我能做的最好的现在。)

我没有使用LINQ,但它是可用的。 MoreLinq也可以使用。

这里是我的课:

public class Student 
{ 
    public Student(string _name, int _age, Exam _lastExam) 
    { 
     name = _name; 
     age = _age; 
     lastExam = _lastExam; 
    } 

    public string name { get; set; } 
    public int age { get; set; } 
    public Exam lastExam { get; set; } 
} 

public class Exam 
{ 
    public Exam(int _correct, int _possible) 
    { 
     correct = _correct; 
     possible = _possible; 
    } 

    public int correct { get; set; } 
    public int possible { get; set; } 
} 

,这里是创造了混乱的代码:

List<List<Student>> lstStudents = new List<List<Student>>(); 
List<Student> lstAStudents = new List<Student>(); 
List<Student> lstDuplicateAStudents = new List<Student>(); 
List<Student> lstBStudents = new List<Student>(); 

// Create a list of some A students 
lstAStudents.Add(new Student("Alex", 14, new Exam(98,100))); 
lstAStudents.Add(new Student("Kim", 13, new Exam(96, 100))); 
lstAStudents.Add(new Student("Brian", 14, new Exam(92, 100))); 
lstStudents.Add(lstAStudents); 

// Create a duplicate list of A students 
lstDuplicateAStudents.Add(new Student("Alex", 14, new Exam(98, 100))); 
lstDuplicateAStudents.Add(new Student("Kim", 13, new Exam(96, 100))); 
lstDuplicateAStudents.Add(new Student("Brian", 14, new Exam(92, 100))); 
lstStudents.Add(lstDuplicateAStudents); 

// Create a list of some B students 
lstBStudents.Add(new Student("John", 13, new Exam(88, 100))); 
lstBStudents.Add(new Student("Jenny", 13, new Exam(80, 100))); 
lstBStudents.Add(new Student("Jamie", 15, new Exam(81, 100))); 
lstStudents.Add(lstBStudents); 
+1

使用'除'Linq方法?创建一个'Set'并将其转换回'List'(删除所有重复项,因为'Set'不能有重复的成员)? – Patashu 2013-03-27 05:01:47

+1

http://stackoverflow.com/questions/5969702/removing-duplicates-in-a-list-with-linq?rq=1确保你选择正确的字段做群组由 – BlackICE 2013-03-27 05:02:49

回答

1

可以使用IEquatable<T>两个StudentExam

public class Student: IEquatable<Student> 
{ 
    ... 

    public bool Equals(Student other) 
    { 
     return name == other.name && age == other.age 
        && lastExam.Equals(other.lastExam); 
    } 

    public override bool Equals(object obj) 
    { 
     Student student = obj as Student; 
     return Equals(student); 
    } 

    public override int GetHashCode() 
    { 
     return name.GetHashCode()^
      age.GetHashCode()^lastExam.GetHashCode(); 
    } 
} 

对于Exam

public class Exam: IEquatable<Exam> 
{ 
    ... 

    public bool Equals(Exam exam) 
    { 
     return exam.correct == correct && exam.possible == possible; 
    } 

    public override bool Equals(object obj) 
    { 
     Exam exam = obj as Exam; 
     return Equals(exam); 
    } 

    public override int GetHashCode() 
    { 
     return correct.GetHashCode()^possible.GetHashCode(); 
    } 
} 

然后建立一个自定义IQualityComparer<T>List<Student>

public class StudentListComparer : IEqualityComparer<List<Student>> 
{ 
    public bool Equals(List<Student> x, List<Student> y) 
    { 
     return x.OrderBy(a => a.name) 
       .SequenceEqual(y.OrderBy(b => b.name)); 
    } 

    public int GetHashCode(List<Student> obj) 
    { 
     return obj.Aggregate(0, (current, t) => current^t.GetHashCode()); 
    } 
} 

然后你可以用Distinct得到结果:

var result = lstStudents.Distinct(new StudentListComparer()); 
+0

非常感谢您花时间写出解决方案。由于“学生”mater的顺序,我只需要将StudentListComparer类中的一行更改为以下内容,以便保持顺序不同的列表:return x.SequenceEqual(y); – 2013-03-27 23:07:25

4

也许你可以拿着一套里面会积累独特的名单:

var set = new HashSet<List<Student>>(new CustomComparer()); 
foreach (List<List<Student>> list in source) 
{ 
    if (set.Contains(list)) 
    continue; 
    set.Add(list) 
} 


public class CustomComparer : IEqualityComparer<List<Student>> 
{ 
    public bool Equals(List<Student> one, List<Student> two) 
    { 
    if (one.Count != two.Count) return false; 

    // simplest possible code to compare two lists 
    // warning: runs in O(N*logN) for each compare 
    return one.OrderBy(s=>s).SequenceEqual(two.OrderBy(s=>s)); 
    } 

    public int GetHashCodeList<Student> item) 
    { 
    int ret = -1; 
    foreach (var s in item) 
     ret ^= s.GetHashCode(); 
    return ret; 
    } 
} 

该解决方案的主要问题是用于比较两个列表的代码< >。包含相同元素的列表是否被认为是相同的?如果是的话,我们需要通过预先排序每个列表来改变顺序(以节省比较时间),或者每次列出每个列表的副本,这会招致额外的时间损失。所以我想主要的问题是你的名单有多大。对于1000个学生/ 100列表中的值,性能问题不应引起注意。

另一个问题是GetHashCode的实现 - 它是O(N),我们无处可以缓存计算值,因为List是一个框架结构。为了解决这个问题,我建议引入StudentList类,它将有比较器(现在我们必须指定它),并获得缓存的哈希代码。

此外,还有更好的实现generic collection equivalence comparer可用。

+0

非常感谢你的回应。就我的具体情况而言,每个列表中的学生的顺序确实很重要。 (我应该指出,因为我的例子不是很好。)我决定标记Cuong Le的答案是正确的,因为它确实(几乎是)我正在寻找的内容,但是你帮助我更好地理解了如何解决这个问题。再次感谢您的回复。 – 2013-03-27 23:02:24