2013-03-27 240 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