Как я могу найти книгу с самой высокой ценой, написанную после 2000 года?

#c# #sql #linq

#c# #sql #linq

Вопрос:

Моя задача — выяснить, какая книга имеет самую высокую цену, написанную после 2000 года, и результатом должна быть книга

Вот данные (важна только та часть, где определены книги ^^):

 static public class SampleData
  {
    static public Publisher[] Publishers =
    {
      new Publisher {Name="FunBooks"},
      new Publisher {Name="Joe Publishing"},
      new Publisher {Name="I Publisher"}
    };

    static public Author[] Authors =
    {
      new Author {FirstName="Johnny", LastName="Good"},
      new Author {FirstName="Graziella", LastName="Simplegame"},
      new Author {FirstName="Octavio", LastName="Prince"},
      new Author {FirstName="Jeremy", LastName="Legrand"}
    };

    static public Subject[] Subjects =
    {
      new Subject {Name="Software development",Description="developing and others"},
      new Subject {Name="Novel",Description="great feelings"},
      new Subject {Name="Science fiction",Description="out of space and everywhere"}
    };

    static public Book[] Books =
    {
      new Book {                                        // [0]
        Title="Funny Stories",
        Publisher=Publishers[0],
        Authors=new[]{Authors[0], Authors[1]},
        PageCount=101,
        Price=25.55M,
        PublicationDate=new DateTime(2004, 11, 10),
        Isbn="0-000-77777-2",
        Subject=Subjects[0]
      },
      new Book {                                        // [1]
        Title="LINQ rules",
        Publisher=Publishers[1],
        Authors=new[]{Authors[2]},
        PageCount=300,
        Price=12M,
        PublicationDate=new DateTime(2007, 9, 2),
        Isbn="0-111-77777-2",
        Subject=Subjects[0]
      },
      new Book {                                        // [2]
        Title="C# on Rails",
        Publisher=Publishers[1],
        Authors=new[]{Authors[2]},
        PageCount=256,
        Price=35.5M,
        PublicationDate=new DateTime(2007, 4, 1),
        Isbn="0-222-77777-2",
        Subject=Subjects[0]
      },
      new Book {                                        // [3]
        Title="All your base are belong to us",
        Publisher=Publishers[1],
        Authors=new[]{Authors[3]},
        PageCount=1205,
        Price=35.5M,
        PublicationDate=new DateTime(2006, 5, 5),
        Isbn="0-333-77777-2",
        Subject=Subjects[2]
      },
      new Book {                                        // [4]
        Title="Bonjour mon Amour",
        Publisher=Publishers[0],
        Authors=new[]{Authors[1], Authors[2]},
        PageCount=50,
        Price=29M,
        PublicationDate=new DateTime(1973, 2, 18),
        Isbn="2-444-77777-2",
        Subject=Subjects[1]
      }
    };

    static public User[] Users = 
    {
        new User{Name="Fred"},
        new User{Name="Barney"},
        new User{Name="Wilma"}
    };

    static public Review[] Reviews =
    {
        new Review{ Book = Books[0], Comments="cc1", Rating=2, User=Users[0]},
        new Review{ Book = Books[0], Comments="cc2", Rating=3, User=Users[2]},
        new Review{ Book = Books[1], Comments="cc3", Rating=1, User=Users[0]},
        new Review{ Book = Books[1], Comments="cc4", Rating=2, User=Users[1]},
        new Review{ Book = Books[1], Comments="cc5", Rating=1, User=Users[2]},
        new Review{ Book = Books[2], Comments="cc6", Rating=3, User=Users[2]},        
        new Review{ Book = Books[3], Comments="cc7", Rating=4, User=Users[2]},
        new Review{ Book = Books[4], Comments="cc8", Rating=5, User=Users[1]}
    };

    static SampleData()
    {
        // Books -- Reviews haben einnen Doppelverweise
        //          daher kann erst hier  Book-->Review gesetzt werden
        Books[0].Reviews = new[] { Reviews[0], Reviews[1] };
        Books[1].Reviews = new[] { Reviews[2], Reviews[3], Reviews[4] };
        Books[2].Reviews = new[] { Reviews[5] };
        Books[3].Reviews = new[] { Reviews[6] };
        Books[4].Reviews = new[] { Reviews[7] };
    }

  }
 

Вот что я написал:

 var query3 =
            (from buch in SampleData.Books
             where buch.PublicationDate.Year > 2000
             select buch.Price).ToList();
        decimal ergebnis3 = query3.Max();

        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("-------------------------Aufgabe 3-------------------------");
        Console.WriteLine("Ermitteln sie das Buch mit dem größten Preis welches nach 2000 geschrieben wurde. Ausgabe ist das Buch:");
        ObjectDumper.Write(ergebnis3);
 

Как я могу объединить цену и название и найти книгу с самой высокой ценой, написанную после 2000 года?

Комментарии:

1. Надеюсь, вы не возражаете, но я отредактировал ваше название. Такие заголовки, как «Как я могу объединить эти два» , могут быть не очень информативными для людей, просматривающих SO или использующих поиск Google в будущем.

2. Большое вам спасибо за исправление ^^

Ответ №1:

 var bookWithHighestPriceAfter2000 = SampleData.Books
    .Where(x => x.PublicationDate.Year > 2000) // Filter books published after 2000
    .OrderByDescending(x => x.Price) // Order by price; highest first
    .First(); // Take the first book
 

Если возможно, что после 2000 года не будет книг, опубликованных, возможно, вы захотите заменить First на FirstOrDefault , который вернет null , а не выдаст исключение.

Ответ №2:

Не по теме: добавление после комментария о сортировке в конце

Поскольку ваш класс SampleData уже имеет свойство Books , запрос довольно прост:

 Book mostExpensiveBookAfter2000 = SampleData.Books
    .Where(book => book.PublicationDate.Year > 2000)
    .OrderByDescending(book => book.Price)
    .FirstOrDefault();
 

Прописью: из всего Books SampleData храните только те книги, значение PublicationDate.Year которых превышает 2000. Упорядочите оставшиеся книги по убыванию Price . Таким образом, самая дорогая книга будет первой. Наконец, возьмите первую или null, если книг не осталось.

Хотя это решение легко понять, если вам нужна только самая дорогая книга, было бы пустой тратой вычислительной мощности на сортировку всех книг.

Рассмотрите возможность использования Enumerable.Агрегировать. Инициализируйте первую книгу как самую дорогую. На каждом этапе агрегирования сравнивайте цену самой дорогой книги с ценой следующей книги и сохраняйте самую дорогую.

 Book mostExpensiveBookAfter2000 = SampleData.Books
    .Where(book => book.PublicationDate.Year > 2000)
    .Aggregate( (mostExpensiveBook, nextBook) =>
         (nextBook.Price > mostExpensiveBook.Price) ?  // is nextBook more expensive?
             nextBook :                          // if so, nextBook becomes mostExpensiveBook
             mostExpensiveBook);                 // if not, keep mostExpensiveBook
 

Таким образом, вы будете перечислять последовательность только один раз.

Эффективен ли OrderBy?

Будет ли OrderBy сортировать полную последовательность, если вы берете только первый элемент, или он будет перечислять последовательность только один раз?

Для этого мы смотрим на справочный источник класса Enumerable

Метод OrderBy создает только объект OrderedEnumerable:

 public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector)
{
    return new OrderedEnumerable<TSource, TKey>(source, keySelector,
       null, false);
}
 

Этот OrderedEnumerale мало что делает, пока вы не начнете перечислять. Первым шагом в перечислении является получение перечислителя:

  public IEnumerator<TElement> GetEnumerator()
 {
     Buffer<TElement> buffer = new Buffer<TElement>(source);
     if (buffer.count > 0)
     {
         EnumerableSorter<TElement> sorter = GetEnumerableSorter(null);
         int[] map = sorter.Sort(buffer.items, buffer.count);
         sorter = null;

         for (int i = 0; i < buffer.count; i  )
         {
             yield return buffer.items[map[i]];
         }
    }
}
 

Таким образом, он помещает полный исходный код в буфер и запрашивает перечислимый сортировщик для сортировки данных в буфере:

 internal int[] Sort(TElement[] elements, int count)
{
    ComputeKeys(elements, count);
    int[] map = new int[count];
    for (int i = 0; i < count; i  ) map[i] = i;

    QuickSort(map, 0, count - 1);
    return map;
}

void QuickSort(int[] map, int left, int right)
{
    do
    {
        int i = left;
        int j = right;
        int x = map[i   ((j - i) >> 1)];
        do
        {
            while (i < map.Length amp;amp; CompareKeys(x, map[i]) > 0) i  ;
            while (j >= 0 amp;amp; CompareKeys(x, map[j]) < 0) j--;
            if (i > j) break;
            if (i < j)
            {
                int temp = map[i];
                map[i] = map[j];
                map[j] = temp;
            }
            i  ;
            j--;
        } while (i <= j);
        if (j - left <= right - i) {
        if (left < j) QuickSort(map, left, j);
        left = i;
    }
    else
    {
        if (i < right) QuickSort(map, i, right);
        right = j;
    }
} while (left < right);
 

Таким образом, кажется, что если вы запрашиваете только перечислитель, полная последовательность будет отсортирована даже до того, как вы выполните свою первую MoveNext() .

Используйте отладчик, чтобы проверить это

Вы можете проверить это, если создадите свой собственный IComparer:

 public class MyComparer : Comparer<int>
{
    private IComparer<int> comparer = Comparer<int>.Defau<
    public override int Compare (int x, int y)
    {
        return this.comparer.Compare(x, y);
        // put your breakpoint here
    }
}

IEnumerable<int> values = new int[] {8, 5, 6, 10, 2, 4 };
IEnumerable<int> orderedValues = values.OrderBy(i => i, new MyComparer());

var enumerator = orderedValues.GetEnumerator();
// if you have a breakpoint in the code above, you see that Compare is called
// many times, before you starte enumerating.

// the compare is not used anymore while you enumerate:
while (enumerator.MoveNext())
{
    int i = enumerator.Current;
    Console.WriteLine(i);
}
 

Комментарии:

1. » было бы пустой тратой вычислительной мощности для сортировки всех книг » Первый пример также требует только одной итерации. Where выдает каждый элемент по мере его повторения OrderByDescending , и, конечно FirstOrDefault , это O(1) операция, когда делегат не предоставлен.

Ответ №3:

Это рабочее решение для вас:

   var result = Books.Where(x => x.PublicationDate.Year > 2000).OrderByDescending(x => x.Price).First();