LINQ对子对象上的对象进行排序

时间:2018-03-14 13:09:22

标签: c# linq

我有一个对象列表(Books),这些对象如下所示:

Class Book
{
    public string book_Name {get; set;}
    public Dictionary<string,string> book_Dictionary {get; set;}

    public Book(string book_Name, Dictionary<string,string> book_Dictionary)
    {
        this.book_Name = book.Name;
        this.book_Dictionary = book_Dictionary;
    }
}

我将这些Book对象编译成书籍列表,所以我有

  List<Book> library;

我想浏览此列表并整理出任何重复的图书对象。 (重复的书籍将是与列表中的任何其他书籍具有相同名称词典的书籍。)

要做到这一点,我正在尝试以下方法:

    private List<Book> removeDuplicateBooks(List<Book> library)
    {
        List<Book> distinctLibrary = library
           .GroupBy(x => new { x.book_Name, x.book_Dictionary })
           .Select(g => g.First())
           .ToList();

        return distinctLibrary;
    }

然而,看起来这并没有删除重复...我的猜测是groupBy以某种方式被抛出一个循环,因为其中一个字典?

编辑:删除歧义 - 当我说它不删除重复时,我的意思是返回的distinctLibrary与库相同(即使库确实包含重复的书籍)。

编辑:示例: 假设我的库包含以下Book对象:

bookNum1:

name: "book1",

dictionary: {
   Key:"foo"
   Value:"bar"

   Key:"balloon"
   Value:"red"
}

bookNum2:

name: "book2",

dictionary: {
   Key:"foo"
   Value:"bar"

   Key:"balloon"
   Value:"red"
}

bookNum3:

name: "book1",

dictionary: {
   Key:"foo"
   Value:"bar"

   Key:"balloon"
   Value:"red"
}

bookNum4:

name: "book1",

dictionary: {
   Key:"fooey"
   Value:"bar"

   Key:"balloon"
   Value:"red"
}

如果我通过removeDuplicates()函数放置这个库,我希望返回一个包含以下书籍对象的库:bookNum1,bookNum2,bookNum4

4 个答案:

答案 0 :(得分:3)

Dictionary<TKey,TValue>不会覆盖EqualsGetHashCode,这就是GroupBy只会比较引用的原因。所有这些词典都是使用new Dictionary...创建的,因此它们是不同的引用。您需要覆盖Equals中的GetHashCodeBook和/或实施IEquatable<Book>和/或提供自定义IEqualityComparer<Book>(针对GroupBy)。

public class Book : IEquatable<Book>
{
    public string BookName { get; }
    public Dictionary<string, string> BookDictionary { get; }

    public Book(string bookName, Dictionary<string, string> bookDictionary)
    {
        this.BookName = bookName;
        this.BookDictionary = bookDictionary ?? throw new ArgumentNullException(nameof(bookDictionary));
    }

    public bool Equals(Book other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }

        if (ReferenceEquals(this, other))
        {
            return true;
        }

        if (!string.Equals(BookName, other.BookName))
            return false;
        if (BookDictionary.Count != other.BookDictionary.Count)
            return false;
        return BookDictionary.All(kv => other.BookDictionary.ContainsKey(kv.Value) 
                                     && other.BookDictionary[kv.Key] == kv.Value);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }

        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        if (obj.GetType() != this.GetType())
        {
            return false;
        }

        return Equals((Book) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int dictHash = 17;
            foreach (KeyValuePair<string, string> kv in this.BookDictionary)
            {
                dictHash = dictHash * 23 + kv.Key.GetHashCode();
                dictHash = dictHash * 23 + (kv.Value ?? "").GetHashCode();
            }

            return ((BookName != null ? BookName.GetHashCode() : 0) * 397) ^ dictHash;
        }
    }
}

现在,您可以将Book本身用作词典中的键或GroupBy的参数:

List<Book> distinctLibrary = library
   .GroupBy(book => book)
   .Select(g => g.First())
   .ToList();

现在你甚至可以使用效率更高(更简单):

List<Book> distinctLibrary = library.Distinct().ToList();

答案 1 :(得分:1)

如果要比较自定义类型,则必须实现IComparable接口。然后,这允许确定对象是否大于&#34;或者&#34;小于&#34;或者相等。

如何实现这取决于您的业务逻辑。

答案 2 :(得分:1)

@TimSchmelter的答案看起来相当不错,但我也想分享我的方法(我认为并且做得很简单:))此外,我也可以特别从@Tim Schmelter那里得到批评者。

您共享的主要方法和虚拟数据。

    static void Main(string[] args)
    {
        Dictionary<string, string> dict = new Dictionary<string, string>()
        {
            { "foo", "bar"},
            { "balloon", "red"}
        };

        Dictionary<string, string> dict2 = new Dictionary<string, string>()
        {
            { "fooey", "bar"},
            { "balloon", "red"}
        };

        var books = new List<Book>();
        books.Add(new Book("book1", dict));
        books.Add(new Book("book2", dict));
        books.Add(new Book("book1", dict));
        books.Add(new Book("book1", dict2));

        var distinctLib = RemoveDuplicateBooks(books);
    }

Linq查询;

    private static List<Book> RemoveDuplicateBooks(List<Book> library)
    {
        var distinctLib = from c in library
                        group c by new
                        { 
                            c.book_Name,
                            c.book_Dictionary
                        } into temp
                        select new Book()
                        {
                            book_Name = temp.First().book_Name,
                            book_Dictionary = temp.First().book_Dictionary
                        };

        return distinctLib.ToList();

    }

返回;

Book1 Book2 Book4

答案 3 :(得分:0)

由于您已经有了删除重复图书的方法,我建议您:

private void removeDuplicateBooks(List<Book> library)
    {
        foreach(Book b in library) {
            // put book b in a new library
            List<Book> distinctLibrary = library.FindAll(b);
            // if you find more than once the book
            if(distinctLibrary.Count > 1) {
                 // delete all copies of b and keep only one
                 library.RemoveAll(b);
                 library.Add(b);
            }
        }
    }

注意:此方法不会返回新库,只会清除deafault库。