Как отсортировать список объектов с полем, содержащим как слова, так и числа? - PullRequest
1 голос
/ 26 мая 2020

На данный момент у меня есть следующий список объектов. Все поля имеют строковый тип.

new PurchaseInvoice{ AccountCode="SUPPLIER1",BookingNo="BKG001",AccountingPeriod="2020002",}
new PurchaseInvoice{ AccountCode="3460",BookingNo="BKG001",AccountingPeriod="2020002",}
new PurchaseInvoice{ AccountCode="5120",BookingNo="BKG001",AccountingPeriod="2020002",}
new PurchaseInvoice{ AccountCode="5120",BookingNo="BKG001",AccountingPeriod="2020002",}
new PurchaseInvoice{ AccountCode=""    ,BookingNo="BKG221",AccountingPeriod="2020002",}
new PurchaseInvoice{ AccountCode="1500",BookingNo="BKG221",AccountingPeriod="2020002",}
new PurchaseInvoice{ AccountCode="3460",BookingNo="BKG221",AccountingPeriod="2020002",}
new PurchaseInvoice{ AccountCode="1500",BookingNo="BKG221",AccountingPeriod="2020005",}
new PurchaseInvoice{ AccountCode="3460",BookingNo="BKG221",AccountingPeriod="2020005",}
new PurchaseInvoice{ AccountCode="5120",BookingNo="BKG221",AccountingPeriod="2020005",}

Это необходимо сначала отсортировать по Отчетному периоду , а затем по AccountCode , но в то же время они должны быть сгруппированы вместе по BookingNo .

Первая строка каждой группы должна содержать Поставщик или пустую строку, если она присутствует.

Пустая строка " "также может быть AccountCode

Ожидаемый результат

SUPPLIER1  BKG001  2020002
3460       BKG001  2020002
5120       BKG001  2020002
5120       BKG001  2020002
           BKG221  2020002
1500       BKG221  2020002
3460       BKG221  2020002
1500       BKG221  2020005
3460       BKG221  2020005
5120       BKG221  2020005

Вот что я пробовал

   //Sort 
            purchaseInvoiceList = purchaseInvoiceList.OrderBy(x => x.AccountingPeriod)
                                            .ThenBy(x => x.AccountCode, new MixedComparer())
                                            .ThenBy(x => x.BookingNo)
                                            .ToList();



 class MixedComparer : IComparer<string>
        {
            public int Compare(string x, string y)
            {
                int xVal, yVal;
                var xIsVal = int.TryParse(x, out xVal);
                var yIsVal = int.TryParse(y, out yVal);

                if (xIsVal && yIsVal)   // both are numbers...
                    return xVal.CompareTo(yVal);
                if (!xIsVal && !yIsVal) // both are strings...
                    return x.CompareTo(y);
                if (xIsVal)             // x is a number, sort first
                    return -1;
                return 1;               // x is a string, sort last
            }
        }

И результат, который я получил

3460       BKG001  2020002
5120       BKG001  2020002
5120       BKG001  2020002
SUPPLIER1  BKG001  2020002
1500       BKG221  2020002
3460       BKG221  2020002
           BKG221  2020002
1500       BKG221  2020005
3460       BKG221  2020005
5120       BKG221  2020005

Как это можно отсортировать по ожидаемому результату? Кто-нибудь может здесь помочь?

Ответы [ 3 ]

2 голосов
/ 26 мая 2020

ваш компаратор сначала сортирует числа, а затем строки. это должно быть наоборот

public int Compare(string x, string y)
        {
            int xVal, yVal;
            var xIsVal = int.TryParse(x, out xVal);
            var yIsVal = int.TryParse(y, out yVal);

            if (xIsVal && yIsVal)   // both are numbers...
                return xVal.CompareTo(yVal);
            if (!xIsVal && !yIsVal) // both are strings...
                return x.CompareTo(y);
            if (xIsVal)             // x is a number, sort last<---
                return 1;
            return -1;               // x is a string, sort first<---
        }

также, вы должны сначала отсортировать по BookingNo, а затем по AccountCode, а затем AccountingPeriod

0 голосов
/ 26 мая 2020

Последовательность вашего OrderBy была немного неправильной в соответствии с вашим желаемым результатом.

*
var sortedList = purchaseInvoiceList.
    OrderBy(x => x.BookingNo).
    ThenBy(x => x.AccountingPeriod).
    ThenBy(x => x.AccountCode, new MixedComparer());

Кроме того, последние два случая вашего MixedComparer были инвертированы, снова в соответствии с вашим желаемым результатом:

public class MixedComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int xVal, yVal, result;
        var xIsVal = int.TryParse(x, out xVal);
        var yIsVal = int.TryParse(y, out yVal);

        if (xIsVal && yIsVal)           // both are numbers...
        { result = xVal.CompareTo(yVal); }
        else if (!xIsVal && !yIsVal)    // both are strings...
        { result = x.CompareTo(y); }
        else if (xIsVal)                // x is a number, sort first
        { result = 1; }
        else result = -1;               // x is a string, sort last
        return result;
    }
}

Вывод:

SUPPLIER1 : BKG001 : 2020002
3460      : BKG001 : 2020002
5120      : BKG001 : 2020002
5120      : BKG001 : 2020002
          : BKG221 : 2020002
1500      : BKG221 : 2020002
3460      : BKG221 : 2020002
1500      : BKG221 : 2020005
3460      : BKG221 : 2020005
5120      : BKG221 : 2020005
0 голосов
/ 26 мая 2020

Если вы хотите, чтобы результаты сначала были сгруппированы по BookingNo, то вам нужно сначала упорядочить результаты по ним (если я неправильно понял, и вы хотите, чтобы две записи с одинаковым BookingNo были разделены, если у них сильно различаются значения AccountingPeriod).

Как отмечали другие, ваш компаратор сначала сортирует числа, поэтому, если вы измените порядок там, он должен работать, если вы хотите, вы также можете обойтись без компаратора: (может быть не так эффективно, я честно не знаю )

purchaseInvoiceList = purchaseInvoiceList
            .Select(Invoice => (Invoice, !int.TryParse(Invoice.AccountCode, out _) && !string.IsNullOrEmpty(Invoice.AccountCode) ? 0 : string.IsNullOrEmpty(Invoice.AccountCode) ? 2 : 1))
            .OrderBy(x => x.Invoice.BookingNo)
            .ThenBy(x => x.Invoice.AccountingPeriod)
            .ThenBy(x => x.Item2)
            .ThenBy(x => x.Invoice.AccountCode)
            .Select(x=>x.Invoice)
            .ToList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...