컬렉션의 모든 객체의 속성에서 .max ()를 수행하는 방법과 최대 값으로 객체를 반환하는 방법 [복제
문제
이 질문은 이미 여기에 답이 있습니다.
두 개의 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);
음?
지금까지의 대답은 훌륭합니다! 그러나 다음과 같은 제약이있는 솔루션이 필요합니다.
- 평범하고 간결한 linq;
- o (n) 복잡성;
- 요소 당 속성을 두 번 이상 평가하지 마십시오.
여기있어:
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가 있기 때문에).