使用 linq 计算列表属性的总和,不包括最小值和最大值

calculate sum of list properties excluding min and max value with linq

这是我目前拥有的:

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

我想做的是排除列表中的最小值和最大值,然后得到总值。

是否可以在一个 linq 语句中完成所有这些操作?

您应该在求和之前预处理列表以排除最小值和最大值。

如果要排除 所有 最小值和最大值,请预先计算这两个值,然后使用 È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);

您可以尝试先对列表进行排序,然后跳过第一项(最少)并从其余项中取出除最后一项(最多)以外的所有项:

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

这不是可以想象的最好的代码,但它确实具有

  • 仅枚举整个集合一次(虽然它确实获得了第一个值三次)。
  • 不需要比保存 IEnumerator 和两个 Tuple<int, int, long, long> 对象多得多的内存(如果使用 OrderByToList 和排序等,则不需要。 ).这让它可以处理任意大的 IEnumerable 集合。
  • 单个 Linq 表达式(这就是您想要的)。
  • 正确处理边缘情况 (values.Count() < 2):
    • 当没有值时,在 IEnumerable 上使用 Min()Max() 将抛出 InvalidOperationException
    • 当只有一个值时,天真的实现会在 IEnumerable 上做类似 Sum() - Min() - Max() 的事情,returns 单个值,取反。

我知道你已经接受了一个答案,但它是:我正在使用单个调用 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;
}