2015-02-11 2 views
0

Это то, что я до сих пор:высчитывает сумма свойств списка за исключением минимального и максимального значения с помощью LINQ

decimal? total = list.Sum(item => item.Score); 

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

Можно ли все это сделать в одном заявлении linq?

+5

Вы можете попробовать что-то вроде list.OrderBy (пункт => item.Score) Пропустите (1) .Снять (list.Count - 2) .sum (пункт => item.Score); Для справки я ненавижу себя сейчас, и это, вероятно, самый неоптимизированный кусок мусора. – wgraham

+0

Вы можете определенно сделать это с помощью аккумулятора с * одной * оценкой перечислимого. (Я имею в виду использование агрегата). –

+3

Зачем вам одно заявление? Не был бы простой 'list.Sum (item => item.Score) - list.Max() - list.Min()' более читабельным? Я сомневаюсь, что повторение списка три раза станет узким местом вашей заявки. –

ответ

10
list.OrderBy(item => item.Score) 
    .Skip(1) 
    .Reverse() 
    .Skip(1) 
    .Sum(item => item.Score); 
+0

Это красиво. – wgraham

+0

Этот запрос дает мне правильный результат, спасибо w.b. – Laziale

+0

Добро пожаловать :) –

-2

Вы должны предварительно обработать список перед суммой, чтобы исключить min и max.

0

Если вы хотите исключить все мин- и макс-значения, предварительно вычислить оба значения, а затем использовать Ènumerable.Where исключить их:

decimal? min = list.Min(item => item.Score); 
decimal? max = list.Max(item => item.Score); 
decimal? total = list 
    .Where(item=> item.Score != min && item.Score != max) 
    .Sum(item => item.Score); 
+2

Это задает вопрос о том, хочет ли он исключить все экземпляры или один экземпляр показателя min/max. –

+0

Что делать, если у меня есть два или три счета, которые являются минимальным или максимальным значением, это исключает все три или только одно? Мне нужно только одно, которое нужно удалить. Спасибо за помощь @Tim – Laziale

+0

@ Laziale: вышеупомянутый подход исключает все минимальные и максимальные значения. Это так, как я понял ваше требование. –

5

Вы можете попробовать заказ списка, а затем пропустить первый пункт (минимум) и принять все, кроме последнего (максимум) от остальных:

decimal? total = list.OrderBy(x => x.Score) 
        .Skip(1) 
        .Take(list.Count - 2) 
        .Sum(x => x.Score); 
+0

Спасибо, сосед, но другое решение работает лучше для меня ... – Laziale

1

Это не самый хороший код можно себе представить, но это ВГА е преимущества

  • перечисляя только через всю коллекцию раз (хотя это действительно получает первое значение в три раза).
  • Не требуется больше памяти для хранения IEnumerator и двух объектов Tuple<int, int, long, long> (которые у вас не было бы при использовании OrderBy, ToList и сортировке и т. Д.). Это позволяет работать с произвольно большими коллекциями IEnumerable.
  • Одно выражение Linq (это то, что вы хотели).
  • Обрабатывает крайние случаи (values.Count() < 2) правильно:
    • , когда нет никаких значений, используя Min() и Max() на IEnumerable выбросит InvalidOperationException
    • , когда есть одна ценность, наивные реализации будут делать что-то вроде Sum() - Min() - Max() на IEnumerable который возвращает одно значение, отрицание.

Я знаю, что вы уже приняли ответ, но вот это: я использую один вызов Enumerable.Aggregate.

public static long SumExcludingMinAndMax(IEnumerable<int> values) 
{ 
    // first parameter: seed (Tuple<running minimum, running maximum, count, running total>) 
    // second parameter: func to generate accumulate 
    // third parameter: func to select final result 
    var result = values.Aggregate(
      Tuple.Create<int, int, long, long>(int.MaxValue, int.MinValue, 0, 0), 
      (accumulate, value) => Tuple.Create<int, int, long, long>(Math.Min(accumulate.Item1, value), Math.Max(accumulate.Item2, value), accumulate.Item3 + 1, accumulate.Item4 + value), 
      accumulate => accumulate.Item3 < 2 ? 0 : accumulate.Item4 - accumulate.Item1 - accumulate.Item2); 

    return result; 
} 
+0

Вы можете поменять «invitations» на 'First' с' int.MaxValue' и 'int.MinValue' в качестве значений прайминга, чтобы избежать доступа к' IEnumerable' более одного раза здесь. Кроме того, если OP обеспокоен производительностью, здесь дешевле использовать изменчивую структуру в качестве аккумулятора, и если он действительно заинтересован в производительности, дешевле сделать все это в простом цикле foreach. Я согласен с духом этого, хотя принятый ответ, хотя и довольно элегантный и прекрасный для небольших коллекций, которые вызывают нечасто, ужасен с точки зрения сложности пространства и времени. –

+0

Вы абсолютно правы в структуре. Я не мог думать о какой-либо существующей «структуре» в BCL, которая соответствовала счету. Хуже того, вы получите 'InvalidOperationException', если используете мой метод без значений. Хуже того, я изначально называл его «AverageExcludingMinAndMax», когда он возвращал сумму. Я обновил свой ответ, чтобы обработать случай нулевого значения и семя с помощью 'int.MinValue' и' int.MaxValue'. –