การเรียงลำดับ IList ใน C #


87

วันนี้ก็เลยเจอปัญหาที่น่าสนใจ เรามีบริการเว็บ WCF ที่ส่งคืน IList ไม่ใช่เรื่องใหญ่จริง ๆ จนอยากจะเรียงลำดับ

ปรากฎว่าอินเทอร์เฟซ IList ไม่มีวิธีการจัดเรียงในตัว

ฉันลงเอยด้วยArrayList.Adapter(list).Sort(new MyComparer())วิธีการแก้ปัญหา แต่ดูเหมือน "สลัม" สำหรับฉัน

ฉันเล่นด้วยการเขียนวิธีการขยายรวมถึงการสืบทอดจาก IList และใช้วิธีการ Sort () ของฉันเองรวมถึงการแคสต์ไปยัง List แต่ไม่มีสิ่งใดที่ดูหรูหราเกินไป

คำถามของฉันคือใครมีวิธีแก้ปัญหาที่สวยงามในการจัดเรียง IList


ทำไมคุณถึงส่งคืน IList ตั้งแต่แรก? จากบริการ WCF?
DaeMoohn

คำตอบ:


55

แล้วการใช้ LINQ To Objects เพื่อจัดเรียงให้คุณล่ะ?

สมมติว่าคุณมีIList<Car>และรถมีEngineทรัพย์สินฉันเชื่อว่าคุณสามารถจัดเรียงได้ดังนี้:

from c in list
orderby c.Engine
select c;

แก้ไข: คุณต้องรีบรับคำตอบที่นี่ เมื่อฉันนำเสนอไวยากรณ์ที่แตกต่างกันเล็กน้อยสำหรับคำตอบอื่น ๆ ฉันจะปล่อยให้คำตอบของฉัน - อย่างไรก็ตามคำตอบอื่น ๆ ที่นำเสนอนั้นใช้ได้เท่าเทียมกัน


3
จะสร้างการแจงนับใหม่ซึ่งอาจไม่เป็นที่ต้องการในบางสถานการณ์ คุณไม่สามารถจัดเรียง IList <T> ในตำแหน่งผ่านอินเทอร์เฟซยกเว้นโดยใช้เมธอด ArrayList.Adapter ในความรู้ของฉัน
Tanveer Badar


61

คำถามนี้เป็นแรงบันดาลใจให้ฉันเขียนบล็อกโพสต์: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

ฉันคิดว่าตามหลักการแล้ว. NET Framework จะรวมวิธีการเรียงลำดับแบบคงที่ที่ยอมรับ IList <T> แต่สิ่งที่ดีที่สุดถัดไปคือการสร้างวิธีการขยายของคุณเอง ไม่ยากเกินไปที่จะสร้างสองวิธีที่จะช่วยให้คุณสามารถจัดเรียง IList <T> ได้เหมือนกับที่คุณทำรายการ <T> เป็นโบนัสคุณสามารถใช้วิธีการขยาย LINQ OrderBy มากเกินไปโดยใช้เทคนิคเดียวกันดังนั้นไม่ว่าคุณจะใช้ List.Sort, IList.Sort หรือ IEnumerable.OrderBy คุณสามารถใช้ไวยากรณ์เดียวกันได้ทุกประการ

public static class SortExtensions
{
    //  Sorts an IList<T> in place.
    public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
    {
        ArrayList.Adapter((IList)list).Sort(new ComparisonComparer<T>(comparison));
    }

    // Sorts in IList<T> in place, when T is IComparable<T>
    public static void Sort<T>(this IList<T> list) where T: IComparable<T>
    {
        Comparison<T> comparison = (l, r) => l.CompareTo(r);
        Sort(list, comparison);

    }

    // Convenience method on IEnumerable<T> to allow passing of a
    // Comparison<T> delegate to the OrderBy method.
    public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison)
    {
        return list.OrderBy(t => t, new ComparisonComparer<T>(comparison));
    }
}

// Wraps a generic Comparison<T> delegate in an IComparer to make it easy
// to use a lambda expression for methods that take an IComparer or IComparer<T>
public class ComparisonComparer<T> : IComparer<T>, IComparer
{
    private readonly Comparison<T> _comparison;

    public ComparisonComparer(Comparison<T> comparison)
    {
        _comparison = comparison;
    }

    public int Compare(T x, T y)
    {
        return _comparison(x, y);
    }

    public int Compare(object o1, object o2)
    {
        return _comparison((T)o1, (T)o2);
    }
}

ด้วยส่วนขยายเหล่านี้ให้จัดเรียง IList ของคุณเหมือนกับที่คุณทำรายการ:

IList<string> iList = new []
{
    "Carlton", "Alison", "Bob", "Eric", "David"
};

// Use the custom extensions:

// Sort in-place, by string length
iList.Sort((s1, s2) => s1.Length.CompareTo(s2.Length));

// Or use OrderBy()
IEnumerable<string> ordered = iList.OrderBy((s1, s2) => s1.Length.CompareTo(s2.Length));

มีข้อมูลเพิ่มเติมในโพสต์: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/


1
แนวทางที่ถูกต้องคือการนำเสนอISortableList<T>อินเทอร์เฟซ (ด้วยวิธีการจัดเรียงส่วนหนึ่งของรายการโดยใช้ตัวเปรียบเทียบบางตัว) List<T>นำไปใช้และมีวิธีการคงที่ซึ่งสามารถจัดเรียงใด ๆIList<T>โดยตรวจสอบว่าได้ใช้งานISortableList<T>หรือไม่หากไม่เป็นเช่นนั้น คัดลอกไปยังอาร์เรย์เรียงลำดับล้างข้อมูลIList<T>และเพิ่มรายการใหม่
supercat

4
คำตอบที่ยอดเยี่ยม! อย่างไรก็ตามคำเตือน: วิธีนี้ถือว่าIList<T> listสามารถส่งไปยังIListอินเทอร์เฟซที่ไม่ใช่ทั่วไปได้ หากคุณเขียนโค้ดคลาสของคุณเองโดยใช้IList<T>อินเทอร์เฟซตรวจสอบให้แน่ใจว่าคุณใช้IListอินเทอร์เฟซที่ไม่ใช่แบบทั่วไปด้วยไม่เช่นนั้นโค้ดจะล้มเหลวด้วยข้อยกเว้นการแคสคลาส
sstan

1
@supercat: ISortableList<T>ข้อเสนออะไรที่ยังไม่มีในIList<T>? หรือถามแตกต่างกันทำไมไม่IList<T>สามารถจัดเรียงในสถานที่โดยไม่ต้องเพิ่มรายการใหม่โดยวิธีการคงที่จินตนาการของคุณ
หรือ Mapper

@ORMapper: หากรายการใช้อาร์เรย์เป็นที่เก็บสำรอง (ทั่วไป แต่ไม่จำเป็น) รูทีนการจัดเรียงที่เข้าถึงองค์ประกอบอาร์เรย์โดยตรงอาจเร็วกว่าที่ต้องผ่านIList<T>อินเทอร์เฟซเพื่อเข้าถึงทุกองค์ประกอบ ความแตกต่างของความเร็วนั้นยอดเยี่ยมพอสมควรที่ในหลาย ๆ กรณีการคัดลอกรายการไปยังอาร์เรย์จัดเรียงอาร์เรย์และคัดลอกรายการกลับทำได้เร็วกว่าการพยายามจัดลำดับขั้นตอนการจัดเรียงรายการให้เข้าที่
supercat

1
ComparisonComparerชั้นไม่จำเป็น คุณสามารถใช้วิธีคงมาตรฐานComparer<T>.Create(comparison)แทนได้
linepogl

9

คุณจะต้องทำอะไรแบบนั้นที่ฉันคิด (แปลงเป็นประเภทที่เป็นรูปธรรมมากขึ้น)

อาจนำไปไว้ใน List ของ T แทนที่จะเป็น ArrayList เพื่อให้คุณได้รับความปลอดภัยในการพิมพ์และตัวเลือกเพิ่มเติมสำหรับวิธีการใช้งานตัวเปรียบเทียบ


4

คำตอบที่ยอมรับโดย @DavidMills นั้นค่อนข้างดี แต่ฉันคิดว่ามันสามารถปรับปรุงได้ ประการแรกไม่จำเป็นต้องกำหนดComparisonComparer<T>คลาสเมื่อเฟรมเวิร์กมีเมธอดแบบคงที่Comparer<T>.Create(Comparison<T>)แล้ว วิธีนี้สามารถใช้เพื่อสร้างได้IComparisonทันที

นอกจากนี้ก็ปลดเปลื้องIList<T>การIListที่มีศักยภาพที่จะเป็นอันตราย ในกรณีส่วนใหญ่ที่ฉันเคยเห็นมีList<T>การIListใช้เครื่องมือใดอยู่เบื้องหลังในการนำไปใช้IList<T>แต่สิ่งนี้ไม่สามารถรับประกันได้และอาจทำให้โค้ดเปราะได้

สุดท้ายList<T>.Sort()วิธีการโอเวอร์โหลดมี 4 ลายเซ็นและมีเพียง 2 แบบเท่านั้นที่ใช้งานได้

  1. List<T>.Sort()
  2. List<T>.Sort(Comparison<T>)
  3. List<T>.Sort(IComparer<T>)
  4. List<T>.Sort(Int32, Int32, IComparer<T>)

ชั้นเรียนด้านล่างใช้List<T>.Sort()ลายเซ็นทั้ง 4 สำหรับIList<T>อินเทอร์เฟซ:

using System;
using System.Collections.Generic;

public static class IListExtensions
{
    public static void Sort<T>(this IList<T> list)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort();
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort();
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(comparison);
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort(comparison);
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, IComparer<T> comparer)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(comparer);
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort(comparer);
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, int index, int count,
        IComparer<T> comparer)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(index, count, comparer);
        }
        else
        {
            List<T> range = new List<T>(count);
            for (int i = 0; i < count; i++)
            {
                range.Add(list[index + i]);
            }
            range.Sort(comparer);
            Copy(range, 0, list, index, count);
        }
    }

    private static void Copy<T>(IList<T> sourceList, int sourceIndex,
        IList<T> destinationList, int destinationIndex, int count)
    {
        for (int i = 0; i < count; i++)
        {
            destinationList[destinationIndex + i] = sourceList[sourceIndex + i];
        }
    }
}

การใช้งาน:

class Foo
{
    public int Bar;

    public Foo(int bar) { this.Bar = bar; }
}

void TestSort()
{
    IList<int> ints = new List<int>() { 1, 4, 5, 3, 2 };
    IList<Foo> foos = new List<Foo>()
    {
        new Foo(1),
        new Foo(4),
        new Foo(5),
        new Foo(3),
        new Foo(2),
    };

    ints.Sort();
    foos.Sort((x, y) => Comparer<int>.Default.Compare(x.Bar, y.Bar));
}

แนวคิดในที่นี้คือการใช้ประโยชน์จากฟังก์ชันการทำงานของข้อมูลพื้นฐานList<T>เพื่อจัดการการเรียงลำดับเมื่อทำได้ อีกครั้งIList<T>การใช้งานส่วนใหญ่ที่ฉันเห็นใช้สิ่งนี้ ในกรณีที่คอลเล็กชันพื้นฐานเป็นประเภทอื่นให้กลับไปสร้างอินสแตนซ์ใหม่ที่List<T>มีองค์ประกอบจากรายการอินพุตใช้เพื่อทำการเรียงลำดับจากนั้นคัดลอกผลลัพธ์กลับไปที่รายการอินพุต สิ่งนี้จะใช้ได้แม้ว่ารายการอินพุตจะไม่ใช้IListอินเทอร์เฟซก็ตาม


3
try this  **USE ORDER BY** :

   public class Employee
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

 private static IList<Employee> GetItems()
        {
            List<Employee> lst = new List<Employee>();

            lst.Add(new Employee { Id = "1", Name = "Emp1" });
            lst.Add(new Employee { Id = "2", Name = "Emp2" });
            lst.Add(new Employee { Id = "7", Name = "Emp7" });
            lst.Add(new Employee { Id = "4", Name = "Emp4" });
            lst.Add(new Employee { Id = "5", Name = "Emp5" });
            lst.Add(new Employee { Id = "6", Name = "Emp6" });
            lst.Add(new Employee { Id = "3", Name = "Emp3" });

            return lst;
        }

**var lst = GetItems().AsEnumerable();

            var orderedLst = lst.OrderBy(t => t.Id).ToList();

            orderedLst.ForEach(emp => Console.WriteLine("Id - {0} Name -{1}", emp.Id, emp.Name));**

ถ้าคุณต้องการให้เป็น DESC แทน ASC ล่ะ?
sam byte

1

พบหัวข้อนี้ในขณะที่ฉันกำลังหาวิธีแก้ปัญหาที่อธิบายไว้ในโพสต์ต้นฉบับ อย่างไรก็ตามไม่มีคำตอบใดตรงกับสถานการณ์ของฉันเลย คำตอบของโบรดี้ค่อนข้างใกล้เคียง นี่คือสถานการณ์และแนวทางแก้ไขของฉันที่ฉันพบ

ฉันมี ILists สองรายการประเภทเดียวกันที่ส่งคืนโดย NHibernate และได้ทำให้ IList ทั้งสองเป็นหนึ่งดังนั้นความจำเป็นในการเรียงลำดับ

เช่นเดียวกับที่โบรดี้กล่าวว่าฉันใช้ ICompare กับวัตถุ (ReportFormat) ซึ่งเป็นประเภทของ IList ของฉัน:

 public class FormatCcdeSorter:IComparer<ReportFormat>
    {
       public int Compare(ReportFormat x, ReportFormat y)
        {
           return x.FormatCode.CompareTo(y.FormatCode);
        }
    }

จากนั้นฉันจะแปลง IList ที่ผสานเป็นอาร์เรย์ประเภทเดียวกัน:

ReportFormat[] myReports = new ReportFormat[reports.Count]; //reports is the merged IList

จากนั้นจัดเรียงอาร์เรย์:

Array.Sort(myReports, new FormatCodeSorter());//sorting using custom comparer

เนื่องจากอาร์เรย์หนึ่งมิติใช้อินเทอร์เฟซSystem.Collections.Generic.IList<T>จึงสามารถใช้อาร์เรย์ได้เช่นเดียวกับ IList ดั้งเดิม


1

มีประโยชน์สำหรับการเรียงลำดับกริดวิธีนี้จะจัดเรียงรายการตามชื่อคุณสมบัติ ตามตัวอย่าง.

    List<MeuTeste> temp = new List<MeuTeste>();

    temp.Add(new MeuTeste(2, "ramster", DateTime.Now));
    temp.Add(new MeuTeste(1, "ball", DateTime.Now));
    temp.Add(new MeuTeste(8, "gimm", DateTime.Now));
    temp.Add(new MeuTeste(3, "dies", DateTime.Now));
    temp.Add(new MeuTeste(9, "random", DateTime.Now));
    temp.Add(new MeuTeste(5, "call", DateTime.Now));
    temp.Add(new MeuTeste(6, "simple", DateTime.Now));
    temp.Add(new MeuTeste(7, "silver", DateTime.Now));
    temp.Add(new MeuTeste(4, "inn", DateTime.Now));

    SortList(ref temp, SortDirection.Ascending, "MyProperty");

    private void SortList<T>(
    ref List<T> lista
    , SortDirection sort
    , string propertyToOrder)
    {
        if (!string.IsNullOrEmpty(propertyToOrder)
        && lista != null
        && lista.Count > 0)
        {
            Type t = lista[0].GetType();

            if (sort == SortDirection.Ascending)
            {
                lista = lista.OrderBy(
                    a => t.InvokeMember(
                        propertyToOrder
                        , System.Reflection.BindingFlags.GetProperty
                        , null
                        , a
                        , null
                    )
                ).ToList();
            }
            else
            {
                lista = lista.OrderByDescending(
                    a => t.InvokeMember(
                        propertyToOrder
                        , System.Reflection.BindingFlags.GetProperty
                        , null
                        , a
                        , null
                    )
                ).ToList();
            }
        }
    }

0

นี่คือตัวอย่างการใช้การพิมพ์ที่แรงขึ้น ไม่แน่ใจว่าเป็นวิธีที่ดีที่สุดหรือไม่

static void Main(string[] args)
{
    IList list = new List<int>() { 1, 3, 2, 5, 4, 6, 9, 8, 7 };
    List<int> stronglyTypedList = new List<int>(Cast<int>(list));
    stronglyTypedList.Sort();
}

private static IEnumerable<T> Cast<T>(IEnumerable list)
{
    foreach (T item in list)
    {
        yield return item;
    }
}

ฟังก์ชัน Cast เป็นเพียงการนำเมธอดส่วนขยายที่มาพร้อมกับ 3.5 มาใช้ใหม่ซึ่งเป็นวิธีการคงที่ปกติ มันค่อนข้างน่าเกลียดและน่าเสียดาย


0

ใน VS2008 เมื่อฉันคลิกที่การอ้างอิงบริการและเลือก "กำหนดค่าการอ้างอิงบริการ" จะมีตัวเลือกให้เลือกวิธีที่ไคลเอ็นต์ยกเลิกการจัดลำดับรายการที่ส่งคืนจากบริการ

โดยเฉพาะอย่างยิ่งฉันสามารถเลือกระหว่าง System.Array, System.Collections.ArrayList และ System.Collections.Generic.List


0
using System.Linq;

var yourList = SomeDAO.GetRandomThings();
yourList.ToList().Sort( (thing, randomThing) => thing.CompareThisProperty.CompareTo( randomThing.CompareThisProperty ) );

สวยจัง!


0

พบโพสต์ที่ดีเกี่ยวกับเรื่องนี้และคิดว่าจะแบ่งปัน ลองดูที่นี่

โดยทั่วไป

คุณสามารถสร้างคลาสและคลาส IComparer ต่อไปนี้

public class Widget {
    public string Name = string.Empty;
    public int Size = 0;

    public Widget(string name, int size) {
    this.Name = name;
    this.Size = size;
}
}

public class WidgetNameSorter : IComparer<Widget> {
    public int Compare(Widget x, Widget y) {
        return x.Name.CompareTo(y.Name);
}
}

public class WidgetSizeSorter : IComparer<Widget> {
    public int Compare(Widget x, Widget y) {
    return x.Size.CompareTo(y.Size);
}
}

ถ้าคุณมี IList คุณสามารถจัดเรียงได้เช่นนี้

List<Widget> widgets = new List<Widget>();
widgets.Add(new Widget("Zeta", 6));
widgets.Add(new Widget("Beta", 3));
widgets.Add(new Widget("Alpha", 9));

widgets.Sort(new WidgetNameSorter());
widgets.Sort(new WidgetSizeSorter());

แต่ตรวจสอบเว็บไซต์นี้เพื่อดูข้อมูลเพิ่มเติม ... ตรวจสอบที่นี่


0

นี่เป็นวิธีแก้ปัญหาที่ถูกต้องหรือไม่?

        IList<string> ilist = new List<string>();
        ilist.Add("B");
        ilist.Add("A");
        ilist.Add("C");

        Console.WriteLine("IList");
        foreach (string val in ilist)
            Console.WriteLine(val);
        Console.WriteLine();

        List<string> list = (List<string>)ilist;
        list.Sort();
        Console.WriteLine("List");
        foreach (string val in list)
            Console.WriteLine(val);
        Console.WriteLine();

        list = null;

        Console.WriteLine("IList again");
        foreach (string val in ilist)
            Console.WriteLine(val);
        Console.WriteLine();

ผลลัพธ์คือ: IList B A C

รายการ A B C

IList อีกครั้ง A B C


1
ใช้ได้ถ้าเป็น List <T> จริงๆ ในบางกรณีคุณมีประเภทอื่นที่ใช้ IList <T> (ตัวอย่างเช่นอาร์เรย์ธรรมดา) ซึ่งการดาวน์แคสต์จะไม่ทำงาน น่าเสียดายที่เมธอด Sort () ไม่ใช่วิธีการขยายไปยัง IList <T>
Cygon

0

นี่ดูง่ายกว่ามากถ้าคุณถามฉัน สิ่งนี้ใช้ได้ผลอย่างสมบูรณ์แบบสำหรับฉัน

คุณสามารถใช้ Cast () เพื่อเปลี่ยนเป็น IList จากนั้นใช้ OrderBy ():

    var ordered = theIList.Cast<T>().OrderBy(e => e);

WHERE T คือประเภทเช่น Model.Employee หรือ Plugin.ContactService.Shared.Contact

จากนั้นคุณสามารถใช้สำหรับลูปและเสร็จสิ้น

  ObservableCollection<Plugin.ContactService.Shared.Contact> ContactItems= new ObservableCollection<Contact>();

    foreach (var item in ordered)
    {
       ContactItems.Add(item);
    }

-1

แปลงของคุณIListเป็นList<T>หรือคอลเลกชันทั่วไปอื่น ๆ จากนั้นคุณสามารถค้นหา / จัดเรียงได้อย่างง่ายดายโดยใช้System.Linqเนมสเปซ (จะจัดหาวิธีการขยายจำนวนมาก)


9
IList<T>การดำเนินการIEnumerable<T>จึงไม่จำเป็นต้องแปลงไปใช้การดำเนินการ Linq
Steve Guidi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.