كيفية أداء .MAX () على خاصية لجميع الكائنات في مجموعة وإرجاع الكائن بأقصى قيمة [مكررة

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

  •  12-09-2019
  •  | 
  •  

سؤال

لدي قائمة بالأشياء التي تحتوي على خصائص كثيرة. القائمة هي إخراج استعلام 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);

هذا أفضل (IMO) من أي من الحلول المقدمة هنا بخلاف الحل الثاني من مهرداد (وهو أساسا كما هو MaxBy):

  • انها o (n) على عكس الإجابة السابقة المقبولة التي تجد الحد الأقصى للقيمة على كل التكرار (مما يجعلها O (n ^ 2))
  • حل الطلب هو O (N Log N)
  • أخذ Max قيمة ثم العثور على العنصر الأول مع هذه القيمة هو O (n)، ولكن تكرار على التسلسل مرتين. حيثما أمكن ذلك، يجب عليك استخدام LinQ في أزياء تمريرة واحدة.
  • إنه لأبسط الكثير من القراءة وفهمه من الإصدار الإجمالي، ويقيم فقط الإسقاط مرة واحدة لكل عنصر

نصائح أخرى

هذا سيتطلب فرز (س (ن سجل ن)) ولكنه بسيط جدا ومرن. ميزة أخرى تكون قادرة على استخدامها مع LinQ إلى SQL:

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

لاحظ أن هذا لديه ميزة تعداد list تسلسل مرة واحدة فقط. بينما قد لا يهم إذا list هو 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. سهل، موجزة لينك.
  2. س (ن) التعقيد؛
  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')

التي تبدو فعالة بالنسبة لي.

بناء على إجابة كاميرون الأولي، إليك ما أضفته للتو في الإصدار المحسن الخاص بي من FloatingWindowhost Silverflow 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);
    }

تجدر الإشارة فيما يتعلق بالتعليم الرمز أعلاه أن CANVAS.ZINDEX هي خاصية متصلة متوفرة ل uelements في حاويات مختلفة، لا تستخدم فقط عند استضافةها في قماش (انظر التحكم في ترتيب التقديم (Zorder) في Silverlight دون استخدام Canvas Control). تخمين المرء يمكن أن يجعل طريقة تمديد أقصى استنساخ و SetBottommost ل UIELENT بسهولة عن طريق تكييف هذا الرمز.

يمكنك أيضا ترقية حل Mehrdad Afshari عن طريق إعادة كتابة طريقة Extention إلى أسرع (وأفضل المظهر)

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 in هناك).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top