簡體   English   中英

在單個 LINQ 查詢中獲取最大值和最小值

[英]Get Max and Min in a single LINQ query

我有一組具有兩個屬性 A 和 B 的對象。我想獲得 A 的最小值和 B 的最大值。

例如

var minA = objects.Min(o => o.A);
var maxB = objects.Max(o => o.B);

使用 LINQ 查詢語法,有沒有辦法做到這一點,以便它只傳遞一次集合?

期望的結果將是匿名類型(例如,results.MinA = x,results.MaxB = y)

Min 和 Max 都是聚合。 一般的linq聚合函數是Aggregate

假設屬性 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);
}

您可以使用Aggregate方法。

var res = new { Min = objects[0].A, Max = objects[0].B }
var res = objects.Aggregate(res, (r, curr) => r.Min = r.Min < curr.A ? r.Min : curr.A; r.Max = r.Max > curr.B ? r.Max : curr.B);

您可以使用YourObject作為結果的容器,並使用Math.MinMath.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 查詢的定義,這非常適合擴展方法。

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();

在 .NET 6 中有新的 LINQ 助手可以解決這個問題:MaxBy 和 MinBy

var people = GetPeople();

var oldest = people.MaxBy(p => p.Age);
var youngest = people.MinBy(p => p.Age);

Console.WriteLine($"The oldest person is {oldest.Age}");
Console.WriteLine($"The youngest person is {youngest.Age}");

如果你想從對象列表中獲取最小值和最大值(並且你不想按屬性分組),那么你可以使用這個 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();

像這樣的東西對你有用 我以前的回答沒有用,

我回去嘗試了幾個不同的答案,最后得到了這個解決方案,以前建議過

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

        var maxmin = new {min = values.Min(), max = values.Max()};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM