컬렉션에있는 모든 객체의 속성에 대해 .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
) 이외의 여기에 제시된 솔루션보다 낫습니다 (IMO ).
- 모든 반복에서 최대 값을 찾는 이전에 허용 된 답변 과 달리 O (n)입니다 (O (n ^ 2)).
- 주문 솔루션은 O (n log n)입니다.
Max
값을 취한 다음 해당 값을 가진 첫 번째 요소를 찾는 것은 O (n)이지만 시퀀스를 두 번 반복합니다. 가능하면 LINQ를 단일 패스 방식으로 사용해야합니다.- 집계 버전보다 읽고 이해하기가 훨씬 간단하며 요소 당 한 번만 투영을 평가합니다.
이것은 정렬 (O (n log n))이 필요하지만 매우 간단하고 유연합니다. 또 다른 장점은 LINQ to 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
MAX를 얻으려는 열을 기준으로 정렬 한 다음 첫 번째를 잡아야한다고 생각합니다. 그러나 동일한 MAX 값을 가진 여러 객체가있는 경우 하나만 가져옵니다.
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 라이브러리 인 FloatingWindowHost에 추가 한 내용이 있습니다 ( http://clipflair.codeplex.com 소스 코드의 FloatingWindowHost.cs에서 복사 ).
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);
}
위의 코드와 관련하여 Canvas.ZIndex는 Canvas에서 호스팅 될 때뿐만 아니라 다양한 컨테이너의 UIElement에 사용할 수있는 첨부 된 속성입니다 ( Canvas 컨트롤을 사용하지 않고 Silverlight에서 렌더링 순서 제어 (ZOrder) 참조 ). 이 코드를 적용하여 UIElement에 대한 SetTopmost 및 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가 있기 때문에).
'Programing' 카테고리의 다른 글
Linux에서 절대 경로로 파일 목록을 생성하려면 어떻게해야합니까? (0) | 2020.03.31 |
---|---|
마지막 생크를 덮어 쓰지 않고 vim에서 삭제하는 방법은 무엇입니까? (0) | 2020.03.31 |
왜 텍스트 영역에 신비한 공백이 가득합니까? (0) | 2020.03.31 |
문자열 배열이 아닌 객체 배열의 Python string.join (list) (0) | 2020.03.30 |
캔버스 요소에서 마우스 클릭의 좌표를 얻으려면 어떻게해야합니까? (0) | 2020.03.30 |