컬렉션의 모든 객체의 속성에서 .max ()를 수행하는 방법과 최대 값으로 객체를 반환하는 방법 [복제

StackOverflow https://stackoverflow.com/questions/1101841

  •  12-09-2019
  •  | 
  •  

문제

이 질문은 이미 여기에 답이 있습니다.

두 개의 int 속성이있는 객체 목록이 있습니다. 목록은 다른 LINQ 쿼리의 출력입니다. 그 물체:

public class DimensionPair  
{
    public int Height { get; set; }
    public int Width { get; set; }
}

가장 큰 목록에서 개체를 찾고 반환하고 싶습니다. Height 재산 가치.

나는 가장 높은 가치를 얻을 수 있습니다. Height 객체 자체는 아닙니다.

LINQ로 이것을 할 수 있습니까? 어떻게?

도움이 되었습니까?

해결책

우리는 있습니다 확장 방법 정확히 이것을 수행합니다 Morelinq. 구현을 볼 수 있지만 기본적으로 데이터를 반복하는 경우가 지금까지 본 최대 요소와 프로젝션에서 생성 된 최대 값을 기억합니다.

귀하의 경우에는 다음과 같은 작업을 수행합니다.

var item = items.MaxBy(x => x.Height);

이것은 Mehrdad의 두 번째 솔루션 이외의 다른 솔루션보다 더 낫습니다 (기본적으로는 기본적으로 동일합니다. MaxBy):

  • O (n)과는 다릅니다 이전에 받아 들여진 답변 모든 반복에서 최대 값을 찾습니다 (O (n^2))
  • 순서 솔루션은 O (n log n)입니다.
  • 복용 Max 그 값으로 첫 번째 요소를 찾는 것은 O (n)이지만 시퀀스를 두 번 반복합니다. 가능하면 단일 패스 방식으로 LINQ를 사용해야합니다.
  • 집계 버전보다 읽고 이해하는 것이 훨씬 간단하며 요소 당 한 번만 프로젝션을 평가합니다.

다른 팁

정렬이 필요합니다 (O (n 통나무 n)) 그러나 매우 간단하고 유연합니다. 또 다른 장점은 LINQ에서 SQL과 함께 사용할 수 있다는 것입니다.

var maxObject = list.OrderByDescending(item => item.Height).First();

이것은 열거하는 이점이 있습니다 list 한 번만 시퀀스. 그럼에도 불구하고 문제가되지 않을 수도 있습니다 list a List<T> 그 동안 변하지 않으면 임의의 경우 중요 할 수 있습니다. IEnumerable<T> 사물. 시퀀스가 다른 열거에서 변하지 않도록 보장하는 것은 없으므로 여러 번 수행하는 방법은 위험 할 수 있습니다 (및 시퀀스의 특성에 따라 비효율적). 그러나 여전히 큰 시퀀스에 이상적인 솔루션이 아닙니다. 나는 당신 자신을 쓰는 것이 좋습니다 MaxObject 정렬 및 기타 물건없이 한 번의 패스로 수행 할 수있는 큰 항목 세트가있는 경우 수동으로 확장 (O (N)) :

static class EnumerableExtensions {
    public static T MaxObject<T,U>(this IEnumerable<T> source, Func<T,U> selector)
      where U : IComparable<U> {
       if (source == null) throw new ArgumentNullException("source");
       bool first = true;
       T maxObj = default(T);
       U maxKey = default(U);
       foreach (var item in source) {
           if (first) {
                maxObj = item;
                maxKey = selector(maxObj);
                first = false;
           } else {
                U currentKey = selector(item);
                if (currentKey.CompareTo(maxKey) > 0) {
                    maxKey = currentKey;
                    maxObj = item;
                }
           }
       }
       if (first) throw new InvalidOperationException("Sequence is empty.");
       return maxObj;
    }
}

다음과 같이 사용하십시오.

var maxObject = list.MaxObject(item => item.Height);

주문을 한 다음 첫 번째 항목을 선택하는 것은 첫 번째 항목 후에 항목을 주문하는 데 많은 시간을 낭비하는 것입니다. 당신은 그것의 순서에 신경 쓰지 않습니다.

대신 집계 함수를 사용하여 찾고있는 내용에 따라 최상의 항목을 선택할 수 있습니다.

var maxHeight = dimensions
    .Aggregate((agg, next) => 
        next.Height > agg.Height ? next : agg);

var maxHeightAndWidth = dimensions
    .Aggregate((agg, next) => 
        next.Height >= agg.Height && next.Width >= agg.Width ? next: agg);

그리고 왜 이것으로 시도하지 않습니까 ??? :

var itemsMax = items.Where(x => x.Height == items.Max(y => y.Height));

또는 더 최적화 :

var itemMaxHeight = items.Max(y => y.Height);
var itemsMax = items.Where(x => x.Height == itemMaxHeight);

음?

지금까지의 대답은 훌륭합니다! 그러나 다음과 같은 제약이있는 솔루션이 필요합니다.

  1. 평범하고 간결한 linq;
  2. o (n) 복잡성;
  3. 요소 당 속성을 두 번 이상 평가하지 마십시오.

여기있어:

public static T MaxBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
    return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
        .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) > 0 ? next : max).Item1;
}

public static T MinBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
    return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
        .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) < 0 ? next : max).Item1;
}

용법:

IEnumerable<Tuple<string, int>> list = new[] {
    new Tuple<string, int>("other", 2),
    new Tuple<string, int>("max", 4),
    new Tuple<string, int>("min", 1),
    new Tuple<string, int>("other", 3),
};
Tuple<string, int> min = list.MinBy(x => x.Item2); // "min", 1
Tuple<string, int> max = list.MaxBy(x => x.Item2); // "max", 4

나는 당신이 최대의 최대를 얻고 싶은 열로 분류하고 첫 번째를 잡는 것이 작동해야한다고 생각합니다. 그러나 동일한 최대 값을 가진 여러 개체가있는 경우 하나만 잡습니다.

private void Test()
{
    test v1 = new test();
    v1.Id = 12;

    test v2 = new test();
    v2.Id = 12;

    test v3 = new test();
    v3.Id = 12;

    List<test> arr = new List<test>();
    arr.Add(v1);
    arr.Add(v2);
    arr.Add(v3);

    test max = arr.OrderByDescending(t => t.Id).First();
}

class test
{
    public int Id { get; set; }
}

nhibernate (nhibernate.linq)에서 다음과 같이 할 수 있습니다.

return session.Query<T>()
              .Single(a => a.Filter == filter &&
                           a.Id == session.Query<T>()
                                          .Where(a2 => a2.Filter == filter)
                                          .Max(a2 => a2.Id));

다음과 같이 SQL을 생성합니다.

select *
from TableName foo
where foo.Filter = 'Filter On String'
and foo.Id = (select cast(max(bar.RowVersion) as INT)
              from TableName bar
              where bar.Name = 'Filter On String')

나에게 꽤 효율적인 것 같습니다.

Cameron의 초기 답변을 기반으로, 여기에 Silverflow Library의 FloatingWindowhost의 강화 된 버전에서 방금 추가 한 내용은 다음과 같습니다 (FloatingWindowHost.cs에서 복사 http://clipflair.codeplex.com 소스 코드)

    public int MaxZIndex
    {
      get {
        return FloatingWindows.Aggregate(-1, (maxZIndex, window) => {
          int w = Canvas.GetZIndex(window);
          return (w > maxZIndex) ? w : maxZIndex;
        });
      }
    }

    private void SetTopmost(UIElement element)
    {
        if (element == null)
            throw new ArgumentNullException("element");

        Canvas.SetZIndex(element, MaxZIndex + 1);
    }

위의 코드는 캔버스에서 호스팅 될 때뿐만 아니라 다양한 컨테이너의 Uielements에 사용할 수있는 첨부 된 속성이라는 위의 코드에 대해 주목할 가치가 있습니다 (캔버스에서 호스팅 할 때뿐만 아니라 캔버스 제어없이 실버 라이트의 렌더링 순서 제어 (Zorder)). 이 코드를 조정하여 Uielement에 대한 최대한 정착하고 SetBottommost 정적 확장 방법을 만들 수 있다고 생각합니다.

연장 메소드를 더 빠르게 (그리고 더 나은 외모)로 작성하여 Mehrdad Afshari의 솔루션을 업그레이드 할 수도 있습니다.

static class EnumerableExtensions
{
    public static T MaxElement<T, R>(this IEnumerable<T> container, Func<T, R> valuingFoo) where R : IComparable
    {
        var enumerator = container.GetEnumerator();
        if (!enumerator.MoveNext())
            throw new ArgumentException("Container is empty!");

        var maxElem = enumerator.Current;
        var maxVal = valuingFoo(maxElem);

        while (enumerator.MoveNext())
        {
            var currVal = valuingFoo(enumerator.Current);

            if (currVal.CompareTo(maxVal) > 0)
            {
                maxVal = currVal;
                maxElem = enumerator.Current;
            }
        }

        return maxElem;
    }
}

그런 다음 사용하십시오.

var maxObject = list.MaxElement(item => item.Height);

이 이름은 C ++를 사용하는 사람들에게 분명합니다 (std :: max_element가 있기 때문에).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top