Как с помощью LINQ найти элемент с минимальным значением поля, а не само это значение?

113
26 марта 2021, 04:30

Есть массив объектов класса A.

class A
{
    pubic double value;
}

Как с помощью LINQ найти элемент с минимальным значением value, а не само это значение, как произошло бы в случае arr.Min(a => a.value)?

Answer 1

Не встречал конструкции, которая выполняла бы это действие за один Linq-запрос. А за два сделать довольно просто:

double min = arr.Min(a => a.value);
var result = arr.FirstOrDefault(a => a.Value == min);

2 вариант: Реализовать в Вашем классе интерфейс IComparable<T>:

class A : IComparable<A>
{
    public double value;
    public int CompareTo(A other)
    {
        return value.CompareTo(other.value);
    }
}

Тогда метод Min() можно вызвать так, и он вернёт объект класса A с минимальным значением поля value:

var result = arr.Min();
Answer 2

С помощью Linq это возможно, но неэффективно. Более простым решением будет написать соответствующий метод; к примеру так:

public static TItem MinByKey<TItem, TKey>(
    this IEnumerable<TItem> items, Func<TItem, TKey> keySelector)
{
    var comparer = Comparer<TKey>.Default;
    var enumerator = items.GetEnumerator();
    if (!enumerator.MoveNext())
        throw new InvalidOperationException("Collection is empty.");
    TItem minItem = enumerator.Current;
    TKey minKey = keySelector(minItem);
    while (enumerator.MoveNext())
    {
        TKey key = keySelector(enumerator.Current);
        if (comparer.Compare(key, minKey) < 0)
        {
            minItem = enumerator.Current;
            minKey = key;
        }
    }
    return minItem;
}

Тогда задача сведётся к вызову

arr.MinByKey(a => a.value);
Answer 3

Раз уж мы подняли этот вопрос: самый простой метод — не строить собственный велосипед, а использовать готовое решение. Например, можно установить пакет MoreLinq через nuget и использовать просто

using MoreLinq;
A best = arr.MinBy(a => a.value);
Answer 4
a.OrderBy(e => e.value).First();

В один короткий запрос, но это не эффективно.

Answer 5
var a = array.Aggregate((r, x) => r.Value < x.Value ? r : x);

Код полностью: https://ideone.com/80oSKs

using System;
using System.Linq;
class A
{
  public double Value;
  public static implicit operator A(double x) { return new A() { Value = x }; }
}
public class Test
{
  public static void Main()
  {
    var array = new A[] { 1.7, 2.3, -7.8, -11.1, 4.5, 42 };
    var a = array.Aggregate((r, x) => r.Value < x.Value ? r : x);
    Console.WriteLine(a.Value);
    Console.WriteLine(Object.ReferenceEquals(a, array[3]));
  }
}

Результат:

-11.1
True
READ ALSO
Как правильно вычислить дату в sql запросе?

Как правильно вычислить дату в sql запросе?

Есть задача, сделать выборку в таблице по возрасту - до ЛЕТДата рождения хранится в типе поля date

94
Вопрос про внешние ключи MySQL

Вопрос про внешние ключи MySQL

Как сделать автоматическую смену внешнего ключа в таблице при удалении родительской записи в другой таблице?

104
yii2 | Работа с константами

yii2 | Работа с константами

У меня есть код

117