C# 在单个LINQ查询中获取最大值和最小值

C# 在单个LINQ查询中获取最大值和最小值,c#,linq,C#,Linq,我有一组具有两个属性的对象,a和B。我想得到a的最小值和B的最大值 乙二醇 使用LINQ查询语法,是否有一种方法可以做到这一点,使其只在集合上传递一次 所需的结果将是匿名类型,例如results.MinA=x,results.MaxB=y 我之前的回答不起作用 我回到这里,尝试了几个不同的答案,最终得到了这个解决方案,如前所述 var values = new List<int> {1, 2, 3, 4, 5}; var maxmin = new {

我有一组具有两个属性的对象,a和B。我想得到a的最小值和B的最大值

乙二醇

使用LINQ查询语法,是否有一种方法可以做到这一点,使其只在集合上传递一次


所需的结果将是匿名类型,例如results.MinA=x,results.MaxB=y 我之前的回答不起作用

我回到这里,尝试了几个不同的答案,最终得到了这个解决方案,如前所述

        var values = new List<int> {1, 2, 3, 4, 5};

        var maxmin = new {min = values.Min(), max = values.Max()};
您可以使用聚合方法

您可以使用对象作为结果的容器,并使用Math.Min和Math.Max整理语法:

var maxmin = objects.Aggregate(objects[0], (i, j) =>
            new YourObject { A = Math.Min(i.A, j.A), B = Math.Max(i.B, j.B) });

最小值和最大值都是聚合。一般的linq聚合函数是

假设属性A是一个整数,B是一个字符串,您可以这样写:

objects.Aggregate(
    new {
        MinA = int.MaxValue,
        MaxB = string.Empty
    },
    (accumulator, o) => new {
        MinA = Math.Min(o.A, accumulator.MinA),
        MaxB = o.B > accumulator.MaxB ? o.B : accumulator.MaxB
    });

我知道您想要一个Linq查询,但我不能停止指出非Linq版本可能更具可读性。比较一下:

IEnumerable<Message> messages = SomeMessages();

Func<DateTime, DateTime, DateTime> min = (dt1, dt2) => dt1 > dt2 ? dt2 : dt1;
Func<DateTime, DateTime, DateTime> max = (dt1, dt2) => dt1 > dt2 ? dt1 : dt2;

// linq version
var result = messages.Aggregate(
    new { StartDate = DateTime.MaxValue, EndDate = DateTime.MinValue }, /* initial value */
    (accumulate, current) => new { StartDate = min(accumulate.StartDate, current.ReceivedTime), EndDate = max(accumulate.EndDate, current.ReceivedTime) });

// non-linq version
DateTime start = DateTime.MaxValue;
DateTime end = DateTime.MinValue;
foreach (DateTime dt in messages.Select(msg => msg.ReceivedTime))
{
    start = min(start, dt);
    end = max(end, dt);
}

如果要从对象列表中获取最小值和最大值,并且不希望按属性分组,则可以使用以下linq:

var val = new List<dynamic>{
    new { A=1, B=1 },
    new { A=2, B=2 },
    new { A=3, B=4 }
}; 

var minMax = val.GroupBy(f => string.Empty).Select(f => new
{
    Min = f.Min(g => g.A),
    Max = f.Max(g => g.B)
}).FirstOrDefault();

根据单个linq查询的定义,这非常适合扩展方法

public static MinAndMax<T> MinAndMax<T>(this IEnumerable<T> source)
    where T : IComparable<T>
{
    Ensure.NotNullOrEmpty(source, nameof(source));
    using (var enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
        {
            var min = enumerator.Current;
            var max = enumerator.Current;
            while (enumerator.MoveNext())
            {
                if (Comparer<T>.Default.Compare(enumerator.Current, min) < 0)
                {
                    min = enumerator.Current;
                }

                if (Comparer<T>.Default.Compare(enumerator.Current, max) > 0)
                {
                    max = enumerator.Current;
                }
            }

            return new MinAndMax<T>(min, max);
        }

        throw new InvalidOperationException("Sequence contains no elements.");
    }
}

public struct MinAndMax<T>
    where T : IComparable<T>
{
    public MinAndMax(T min, T max)
    {
        this.Min = min;
        this.Max = max;
    }

    public T Min { get; }

    public T Max { get; }
}

与非优化解没有任何区别。它实际上不起作用,因为o不是IEnumerable,等等,所以没有最小或最大函数。因此,它不会构建一行let min=o.Mino=>o.AThe问题询问LINQ查询语法,与lambda扩展方法相反。。。不确定这对cyrus是否重要。@MattTester我认为他首先想要单通道,其次是查询样式,为什么不编写自己的扩展方法,遍历对象范围一次并返回元组或其他什么?这几乎是猜测,所以我道歉,但是在一次传递中获得min和maz是这样的:如果num>max,那么max。否则如果num操作符,但只是为了说明。LINQ版本更可读,因为当你阅读源代码时,你不必再引用变量和其他需要在头脑中跟踪的状态。当代码转换数据时,您的眼睛只关注数据转换的位置。就像书中的故事。。你从左到右,从上到下阅读。好吧,现在明白了,关键字是passs over the set once。Min和Max是聚合的抽象,它们都实现了内部foreach means 2个往返。通过@Cirdec解决聚合是完全更好的方法。val.Aggregate min:int.MinValue,max:int.MaxValue,累加器,o=>min:Math.Mino.A,acculator.min,max:Math.Maxo.B,acculator.max;为什么要引入新类型,而不使用ValueTuple?publicstatictmin,tmaxminandmax…对于公共方法,我更喜欢命名类型,但我已经老了。具有命名字段的元组是另一种选择。一个小而平凡的结构没有巨大的缺点。
var val = new List<dynamic>{
    new { A=1, B=1 },
    new { A=2, B=2 },
    new { A=3, B=4 }
}; 

var minMax = val.GroupBy(f => string.Empty).Select(f => new
{
    Min = f.Min(g => g.A),
    Max = f.Max(g => g.B)
}).FirstOrDefault();
public static MinAndMax<T> MinAndMax<T>(this IEnumerable<T> source)
    where T : IComparable<T>
{
    Ensure.NotNullOrEmpty(source, nameof(source));
    using (var enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
        {
            var min = enumerator.Current;
            var max = enumerator.Current;
            while (enumerator.MoveNext())
            {
                if (Comparer<T>.Default.Compare(enumerator.Current, min) < 0)
                {
                    min = enumerator.Current;
                }

                if (Comparer<T>.Default.Compare(enumerator.Current, max) > 0)
                {
                    max = enumerator.Current;
                }
            }

            return new MinAndMax<T>(min, max);
        }

        throw new InvalidOperationException("Sequence contains no elements.");
    }
}

public struct MinAndMax<T>
    where T : IComparable<T>
{
    public MinAndMax(T min, T max)
    {
        this.Min = min;
        this.Max = max;
    }

    public T Min { get; }

    public T Max { get; }
}
var minMax = xs.MinAndMax();