개체의 속성별로 List <T>를 정렬하는 방법


1248

나는라는 클래스가 Order속성 등이 OrderId, OrderDate, Quantity,와 Total. 이 Order수업 목록이 있습니다 .

List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders

이제 Order객체 의 한 속성을 기준으로 목록을 정렬하려고합니다. 예를 들어 주문 날짜 또는 주문 ID별로 정렬해야합니다.

C #에서 어떻게 할 수 있습니까?


9
잠깐만-주문 날짜 주문 ID를 기준으로 정렬 하시겠습니까? 대부분의 답변은 당신이 오직 하나만 정렬한다고 가정하는 것처럼 보입니다.
GalacticCowboy

@GalacticCowboy, @GenericTypeTea : 질문에 " 주문 객체의 속성을 기준으로 목록을 정렬하고 싶다"고 말하지만 다른 곳에서는 완전히 명확하지 않다는 데 동의합니다. 어느 쪽이든, 하나 이상의 속성으로 정렬 해야하는지 여부에 큰 차이가 없습니다.
LukeH

@LukeH-방금 다시 읽었고 동의합니다. '하나의 속성을 기준으로 목록을 정렬하고 싶습니다 ...'및 '주문 날짜 또는 주문 ID'. 모든 답변 사이에 모든 기초가 포함되어 있습니다 :).
djdd87

답변:


1807

내가 생각할 수있는 가장 쉬운 방법은 Linq를 사용하는 것입니다.

List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();

22
내림차순으로 어떻게 정렬 할 수 있습니까?
귀여운 곰

229
@BonusKun List <주문> SortedList = objListOrder. OrderByDescending (o => o.OrderDate) .ToList ();
javajavajavajavajava

77
이렇게하면 메모리의 모든 항목이 포함 된 완전히 새로운 목록이 생성되므로 성능 측면에서 문제가 될 수 있습니다.
staafl

14
@staafl은 listWithObjects = listWithObjects.OrderByDescending(o => o.Status).ToList();그러한 노력으로 충분할까요?
Andrew Grinder

28
@staafl 우리는 내가 알고있는 한 객체 자체를 복제하지 않고 객체 참조 목록을 주문하고 있습니다. 이것이 참조 목록에 사용 된 메모리를 두 배로 늘리는 동안 실제로 모든 객체 자체를 복제하는 것만 큼 나쁘지는 않습니다. 따라서 거대한 데이터 세트를 처리하고 메모리에 보관하는 시나리오 이외의 대부분의 시나리오에서는 이미 문제가됩니다. 충분합니다.
나사로

798

목록을 적절하게 정렬 해야하는 경우 대리자를 Sort전달 하여 메소드를 사용할 수 있습니다 Comparison<T>.

objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

제자리에서 정렬하는 대신 새로운 정렬 된 시퀀스를 만드는 것을 선호한다면 OrderBy다른 답변에서 언급 한 것처럼 LINQ의 방법을 사용할 수 있습니다 .


30
예, 이것은 "올바른"답변이며 새로운 IEnumerable을 만든 다음 새 목록으로 변환하는 것보다 훨씬 효율적입니다.
Jonathan Wood

45
물론, 당신은, 일종의 스왑 내림차순 필요가있는 경우 xy화살표의 오른쪽에 =>.
Jeppe Stig Nielsen

15
실제 목록을 실제로 정렬하는 실제 답변
Mitchell Currie

3
@PimBrouwers 더 나은 옵션 기간입니다. 사용중인 메모리 양에 문제가없는 경우에도이 솔루션은 매우 비싼 불필요한 메모리 할당을 방지합니다. 이 옵션은 코드 단위로 간단하고 훨씬 빠릅니다.
Cdaragorn

12
@JonSchneider For Nullable<>( DateTime?예제로 사용) .Sort((x, y) => Nullable.Compare(x.OrderDate, y.OrderDate))null이 아닌 모든 값을 선행하는 것으로 간주하는 null을 사용할 수 있습니다 . 와 정확히 동일합니다 .Sort((x, y) => Comparer<DateTime?>.Default.Compare(x.OrderDate, y.OrderDate).
Jeppe Stig Nielsen 님이

219

.Net2.0에서 LINQ없이이 작업을 수행하려면

List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
    delegate(Order p1, Order p2)
    {
        return p1.OrderDate.CompareTo(p2.OrderDate);
    }
);

.Net3.0을 사용하는 경우 LukeH의 대답 은 당신이 따르는 입니다.

여러 속성을 정렬하려면 대리인 내에서 계속 수행 할 수 있습니다. 예를 들면 다음과 같습니다.

orderList.Sort(
    delegate(Order p1, Order p2)
    {
        int compareDate = p1.Date.CompareTo(p2.Date);
        if (compareDate == 0)
        {
            return p2.OrderID.CompareTo(p1.OrderID);
        }
        return compareDate;
    }
);

이렇게하면 내림차순 으로 날짜가 오름차순으로 표시됩니다.

그러나 코드 재사용이없는 많은 장소를 의미하기 때문에 대표를 고수하지 않는 것이 좋습니다. 를 구현하고 메소드에 IComparer전달해야합니다 Sort. 여기를 참조 하십시오 .

public class MyOrderingClass : IComparer<Order>
{
    public int Compare(Order x, Order y)
    {
        int compareDate = x.Date.CompareTo(y.Date);
        if (compareDate == 0)
        {
            return x.OrderID.CompareTo(y.OrderID);
        }
        return compareDate;
    }
}

그런 다음이 IComparer 클래스를 사용하려면 인스턴스화하여 Sort 메서드에 전달하십시오.

IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);

1
훌륭한 답변이며 더 나은 캡슐화를 제공하는 원래 목록 (LINQ 버전에서 항상 수행)을 다시 초기화하는 것을 보호하므로 올바른 답변이어야합니다.
b

@ 원턴 아이디어는 다른 IComparer구현 을 가질 수 있고 다형성 동작을 제공하는 것입니다.
radarbob

나를 위해 일했다. 정렬 옵션이있는 버전을 게시했습니다.
잭 그리핀

109

목록을 주문하는 가장 간단한 방법은 OrderBy

 List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ToList();

다음 SQL 쿼리와 같은 여러 열로 주문하려는 경우.

ORDER BY OrderDate, OrderId

이를 달성하기 위해 ThenBy다음과 같이 사용할 수 있습니다 .

  List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();

32

당신이 말한대로 Linq없이 그것을하는 것 :

public class Order : IComparable
{
    public DateTime OrderDate { get; set; }
    public int OrderId { get; set; }

    public int CompareTo(object obj)
    {
        Order orderToCompare = obj as Order;
        if (orderToCompare.OrderDate < OrderDate || orderToCompare.OrderId < OrderId)
        {
            return 1;
        }
        if (orderToCompare.OrderDate > OrderDate || orderToCompare.OrderId > OrderId)
        {
            return -1;
        }

        // The orders are equivalent.
        return 0;
    }
}

그런 다음 주문 목록에서 .sort ()를 호출하십시오.


3
한 가지 반드시 첫 번째 테스트 nullas캐스트. as(ha, ha) (Order)obj가 실패 할 때 예외를 던지기 때문에의 요점은 무엇입니까 ? if(orderToCompare == null) return 1;.
radarbob

인터페이스 사용에 +1 : 코드를보다 쉽게 ​​유지 관리하고 객체의 기능을 명확하게 노출시킵니다.
AeonOfTime

Bill과 Ted가 나타나면 2014 년 10 월 16 일로 돌아가서 실수를 바로 잡을 수 있도록 요청 합니다. 캐스트가 실패하면 as반환 null됩니다. 그러나 적어도 널 테스트는 옳습니다.
radarbob

@radarbob 그래 .. 죄송합니다. :) 그러나! 이 함수는 목록에 대한 정렬에 의해 자동으로 사용 되도록되어 있으므로 불필요한 경비 문을 피할 수있을 정도로 버그를 작성하지 않은 2014 년 List<Order>유형과 일치하도록 유형을 보장해야합니다.as
Jimmy Hoffa

흥미로운 점을 보장해야합니다 . 잘 캡슐화되어 있으면 a를 전달하는 것 외에는 호출 할 수 없습니다 List<Order>. 그러나 당신과 나는 둘 다 자기 캡슐화 된 프로그래머를 만났습니다. 무언의 가정은 "이 코드를 작성하여 잘못 사용되지 않을 것입니다"
radarbob

26

고전적인 객체 지향 솔루션

먼저 LINQ의 훌륭함을 기뻐해야합니다. 이제 우리는 그 길을 벗어났습니다.

JimmyHoffa의 변형입니다. 제네릭을 사용하면 CompareTo매개 변수가 형식이 안전 해집니다.

public class Order : IComparable<Order> {

    public int CompareTo( Order that ) {
        if ( that == null ) return 1;
        if ( this.OrderDate > that.OrderDate) return 1;
        if ( this.OrderDate < that.OrderDate) return -1;
        return 0;
    }
}

// in the client code
// assume myOrders is a populated List<Order>
myOrders.Sort(); 

이 기본 정렬은 물론 재사용 할 수 있습니다. 즉, 각 클라이언트는 정렬 논리를 중복해서 다시 작성할 필요가 없습니다. "1"과 "-1"(또는 선택한 논리 연산자)을 바꾸면 정렬 순서가 반대로됩니다.


List에서 객체를 정렬하는 간단한 방법입니다. 그러나 왜 당신이 1을 반환하는지 이해하지 못합니다 (= = null)?
Loc Huynh

4
그것은 this객체가 null보다 크다는 것을 의미 합니다. 정렬을 위해 null 개체 참조는 "보다 작습니다" this개체입니다. 그것이 null을 정렬하는 방법을 정의하기로 결정한 방식입니다.
radarbob 2016 년

18

// gridview와 함께 사용하기위한 완전히 일반적인 정렬

public List<T> Sort_List<T>(string sortDirection, string sortExpression, List<T> data)
    {

        List<T> data_sorted = new List<T>();

        if (sortDirection == "Ascending")
        {
            data_sorted = (from n in data
                              orderby GetDynamicSortProperty(n, sortExpression) ascending
                              select n).ToList();
        }
        else if (sortDirection == "Descending")
        {
            data_sorted = (from n in data
                              orderby GetDynamicSortProperty(n, sortExpression) descending
                              select n).ToList();

        }

        return data_sorted;

    }

    public object GetDynamicSortProperty(object item, string propName)
    {
        //Use reflection to get order type
        return item.GetType().GetProperty(propName).GetValue(item, null);
    }

4
또는, 당신은 알고있다 data.OrderBy(). 바퀴를 다시 발명하는 것이 조금 더 쉽습니다.
gunr2171 2016 년

4
아이디어는 이것이 모든 유형의 객체에서 작동한다는 것입니다. OrderBy ()는 강력한 형식의 개체에서만 작동합니다.
roger

1
제 생각에는 그리드 데이터를 정렬하는 가장 간단하고 유용한 솔루션입니다!
kostas ch.

6

다음은 목록의 추가 사본을 작성하지 않는 일반적인 LINQ 확장 방법입니다.

public static void Sort<T,U>(this List<T> list, Func<T, U> expression)
    where U : IComparable<U>
{
    list.Sort((x, y) => expression.Invoke(x).CompareTo(expression.Invoke(y)));
}

그것을 사용하려면 :

myList.Sort(x=> x.myProperty);

최근에을 수락하는 추가 항목을 작성 ICompare<U>하여 비교를 사용자 정의 할 수 있습니다. 이것은 자연 문자열 정렬을 수행해야 할 때 유용했습니다.

public static void Sort<T, U>(this List<T> list, Func<T, U> expression, IComparer<U> comparer)
    where U : IComparable<U>
{    
    list.Sort((x, y) => comparer.Compare(expression.Invoke(x), expression.Invoke(y)));
}

나는 이것을 구현했고 잘 작동한다. "isAscending = true"매개 변수를 추가했습니다. 내림차순으로 정렬하려면 두 Invoke () 메서드 내에서 x와 y를 바꾸십시오. 감사.
Rob L

식을 컴파일하려는 경우 대리자를 수락해도됩니다. 또한이 방법은 선택기가 부작용을 일으키거나 계산 비용이 많이 들거나 결정적이지 않은 경우 큰 문제가 있습니다. 선택한 표현식을 기반으로 적절한 정렬을 수행하려면 목록의 항목 당 선택기를 한 번만 호출해야합니다.
Servy

@ Servy-이것들은 모두 유효한 포인트이지만 솔직히 말하면 제안을 구현하는 방법을 잘 모르겠습니다 (특히 선택기가 부작용을 일으키는 것을 막을 수는 없습니다 ...?). 나는 그들을 대표단으로 바꿨다. 이러한 변경을 수행하는 방법을 알고 있다면 코드를 편집하게되어 기쁩니다.
Peter

3
@Peter 대리인이 부작용을 일으키는 것을 막을 수 없습니다 . 당신이 할 수있는 일은 객체 당 한 번 이상 호출되지 않도록하는 것입니다. 즉, 각 객체의 값을 계산하고 객체 / 예상 값 쌍을 저장 한 다음 정렬 합니다 . OrderBy모든 것을 내부적으로 수행합니다.
Servy

void이 정신 으로 확장 방법 을 작성하는 또 다른 좋은 방법 : 방법 static class Extensions { public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector) { self.SortBy(keySelector, Comparer<TKey>.Default); } public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) { self.Sort((x, y) => comparer.Compare(keySelector(x), keySelector(y))); } }으로 보완 할 수 있습니다 SortByDescending. @Servy의 의견 은 내 코드에도 적용됩니다!
Jeppe Stig Nielsen

4

LINQ 사용

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderDate)
                   .ToList();

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderId)
                   .ToList();

3
//Get data from database, then sort list by staff name:

List<StaffMember> staffList = staffHandler.GetStaffMembers();

var sortedList = from staffmember in staffList
                 orderby staffmember.Name ascending
                 select staffmember;

3

로저 버전의 개선.

GetDynamicSortProperty의 문제점은 속성 이름 만 가져 오지만 GridView에서 NavigationProperties를 사용하면 어떻게됩니까? null을 발견하기 때문에 예외를 보냅니다.

예:

"Employee.Company.Name;"이 충돌합니다 ... "Name"만 매개 변수로 사용하여 값을 가져올 수 있습니다.

탐색 속성별로 정렬 할 수있는 개선 된 버전이 있습니다.

public object GetDynamicSortProperty(object item, string propName)
    {
        try
        {                 
            string[] prop = propName.Split('.'); 

            //Use reflection to get order type                   
            int i = 0;                    
            while (i < prop.Count())
            {
                item = item.GetType().GetProperty(prop[i]).GetValue(item, null);
                i++;
            }                     

            return item;
        }
        catch (Exception ex)
        {
            throw ex;
        }


    } 

3

속성 선택에 대해 좀 더 일반적인 작업을 수행 할 수 있지만 '주문'의 경우 선택하는 유형에 대해 구체적 일 수 있습니다.

함수를 일반적인 함수로 작성하십시오.

public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector)
        {
            return (from order in orders
                    orderby propertySelector(order)
                    select order).ToList();
        } 

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

var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate);

당신은 훨씬 더 일반적이고 주문하려는 것에 대해 개방형을 정의 할 수 있습니다.

public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector)
        {
            return (from item in collection
                    orderby propertySelector(item)
                    select item).ToList();
        } 

같은 방식으로 사용하십시오.

var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate);

LINQ 스타일 'OrderBy'를 수행하는 어리 석고 불필요한 복잡한 방법이지만 일반적인 방법으로 구현하는 방법에 대한 단서를 제공 할 수 있습니다


3

테스트 한 결과 샘플 코드로 @LukeH의 답변을 작성해 드리겠습니다.

public class Order
{
    public string OrderId { get; set; }
    public DateTime OrderDate { get; set; }
    public int Quantity { get; set; }
    public int Total { get; set; }

    public Order(string orderId, DateTime orderDate, int quantity, int total)
    {
        OrderId = orderId;
        OrderDate = orderDate;
        Quantity = quantity;
        Total = total;
    }
}

public void SampleDataAndTest()
{
    List<Order> objListOrder = new List<Order>();

    objListOrder.Add(new Order("tu me paulo ", Convert.ToDateTime("01/06/2016"), 1, 44));
    objListOrder.Add(new Order("ante laudabas", Convert.ToDateTime("02/05/2016"), 2, 55));
    objListOrder.Add(new Order("ad ordinem ", Convert.ToDateTime("03/04/2016"), 5, 66));
    objListOrder.Add(new Order("collocationem ", Convert.ToDateTime("04/03/2016"), 9, 77));
    objListOrder.Add(new Order("que rerum ac ", Convert.ToDateTime("05/02/2016"), 10, 65));
    objListOrder.Add(new Order("locorum ; cuius", Convert.ToDateTime("06/01/2016"), 1, 343));


    Console.WriteLine("Sort the list by date ascending:");
    objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    Console.WriteLine("Sort the list by date descending:");
    objListOrder.Sort((x, y) => y.OrderDate.CompareTo(x.OrderDate));
    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    Console.WriteLine("Sort the list by OrderId ascending:");
    objListOrder.Sort((x, y) => x.OrderId.CompareTo(y.OrderId));
    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    //etc ...
}


1

LiNQ 활용 OrderBy

List<Order> objListOrder=new List<Order> ();
    objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();

1

GenericTypeTea 의 비교자를 기반으로 :
정렬 플래그를 추가하여 더 많은 유연성을 얻을 수 있습니다.

public class MyOrderingClass : IComparer<Order> {  
    public int Compare(Order x, Order y) {  
        int compareDate = x.Date.CompareTo(y.Date);  
        if (compareDate == 0) {  
            int compareOrderId = x.OrderID.CompareTo(y.OrderID);  

            if (OrderIdDescending) {  
                compareOrderId = -compareOrderId;  
            }  
            return compareOrderId;  
        }  

        if (DateDescending) {  
            compareDate = -compareDate;  
        }  
        return compareDate;  
    }  

    public bool DateDescending { get; set; }  
    public bool OrderIdDescending { get; set; }  
}  

이 시나리오에서는 정렬 속성을 설정하기 위해 명시 적 으로 MyOrderingClass 로 인스턴스화해야합니다 ( IComparer 대신 )
.

MyOrderingClass comparer = new MyOrderingClass();  
comparer.DateDescending = ...;  
comparer.OrderIdDescending = ...;  
orderList.Sort(comparer);  

1

위의 답변 중 어느 것도 나를 위해 충분히 일반적이지 않았으므로 이것을 만들었습니다.

var someUserInputStringValue = "propertyNameOfObject i.e. 'Quantity' or 'Date'";
var SortedData = DataToBeSorted
                   .OrderBy(m => m.GetType()
                                  .GetProperties()
                                  .First(n => 
                                      n.Name == someUserInputStringValue)
                   .GetValue(m, null))
                 .ToList();

대규모 데이터 세트에주의하십시오. 쉬운 코드이지만 컬렉션이 크고 컬렉션의 객체 유형에 많은 필드가 있으면 문제가 발생할 수 있습니다. 런타임은 NxM입니다. 여기서 :

N = 컬렉션의 요소 수

M = 객체 내 속성 수


1

nullable 형식으로 작업하는 사람 Value은을 사용해야 CompareTo합니다.

objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));


0

성능 관점에서 데이터가 결과에 추가 될 때 정렬되도록 정렬 된 목록을 사용하는 것이 가장 좋습니다. 다른 접근 방식은 데이터에 대해 적어도 하나의 추가 반복이 필요하며 대부분 데이터 복사본을 생성하므로 성능뿐만 아니라 메모리 사용에도 영향을 미칩니다. 수백 가지 요소에는 문제가되지 않을 수 있지만 특히 많은 동시 요청이 동시에 정렬 될 수있는 서비스에서는 수천 가지에 해당 될 수 있습니다. System.Collections.Generic 네임 스페이스를보고 List 대신 정렬이있는 클래스를 선택하십시오.

가능하면 리플렉션을 사용하여 일반적인 구현을 피하십시오. 성능 문제가 발생할 수도 있습니다.


이것이 어떻게 더 성능이 좋을 수 있습니까? 정렬 된 목록에 데이터를 삽입하는 것은 O (n)이므로 n 개의 항목을 추가하는 것은 O (n ^ 2)입니다. 많은 동시 항목을 추가하려고합니까? 항목을 추가하는 동안 추가 CPU주기가 발생하는 상황이 있지만 이는 일반적인 해결책이 아닙니다.
John La Rooy

0

다음 코드가 있다고 가정합니다.이 코드에는 정렬하려는 몇 가지 속성이있는 여객 클래스가 있습니다.

public class Passenger
{
    public string Name { get; }
    public string LastName { get; }
    public string PassportNo { get; }
    public string Nationality { get; }

    public Passenger(string name, string lastName, string passportNo, string nationality)
    {
        this.Name = name;
        this.LastName = lastName;
        this.PassportNo = passportNo;
        this.Nationality = nationality;
    }

    public static int CompareByName(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.Name, passenger2.Name);
    }

    public static int CompareByLastName(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.LastName, passenger2.LastName);
    }

    public static int CompareNationality(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.Nationality, passenger2.Nationality);
    }
}

public class TestPassengerSort
{
    Passenger p1 = new Passenger("Johon", "Floid", "A123456789", "USA");
    Passenger p2 = new Passenger("Jo", "Sina", "A987463215", "UAE");
    Passenger p3 = new Passenger("Ped", "Zoola", "A987855215", "Italy");

    public void SortThem()
    {
        Passenger[] passengers = new Passenger[] { p1, p2, p3 };
        List<Passenger> passengerList = new List<Passenger> { p1, p2, p3 };

        Array.Sort(passengers, Passenger.CompareByName);
        Array.Sort(passengers, Passenger.CompareByLastName);
        Array.Sort(passengers, Passenger.CompareNationality);

        passengerList.Sort(Passenger.CompareByName);
        passengerList.Sort(Passenger.CompareByLastName);
        passengerList.Sort(Passenger.CompareNationality);

    }
}

따라서 Composition 대리자를 사용하여 정렬 구조를 구현할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.