【问题标题】:How to sort list of objects with field containing both words and numbers?如何对包含单词和数字的字段的对象列表进行排序?
【发布时间】:2020-05-26 06:18:21
【问题描述】:

目前我有以下对象列表。所有字段都是字符串类型。

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

如何根据预期输出对其进行排序?请问有人可以帮忙吗?

【问题讨论】:

  • 我相信您只是缺少GroupBy(x =&gt; x.BookingNo)docs.microsoft.com/en-us/dotnet/api/…
  • 我看到的预期输出的不同之处在于您将数字放在字符串之前。无论如何,这就是您的比较器所做的。查看代码的 cmets。
  • 每个组的第一行应该有供应商或空字符串(如果存在)是什么意思。你只有一个 SUPPLIER1 和一个空字符串。实际上var result = purchaseInvoiceList.OrderBy(x =&gt; x.BookingNo).ThenBy(x =&gt; x.AccountingPeriod).ToList(); 给了你一个预期的顺序,让第一列保持原样

标签: c# .net linq sorting object


【解决方案1】:

您的比较器首先对数字进行排序,然后对字符串进行排序。它需要反过来

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<---
        }

另外,您应该首先按BookingNoAccountCode 排序,然后是AccountingPeriod

【讨论】:

    【解决方案2】:

    如果您想首先按 BookingNo 对结果进行分组,那么实际上您需要先按这些排序,(除非我误解了,并且如果具有完全不同的 AccountingPeriod,您希望将具有相同 BookingNo 的两条记录分开)。

    正如其他人指出的那样,您的比较器首先对数字进行排序,因此如果您在此处切换顺序,它应该可以工作,如果您愿意,您也可以不使用比较器:(可能效率不高,我真的不知道)

    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();
    

    【讨论】:

      【解决方案3】:

      您的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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-24
        • 2022-11-24
        • 2011-05-04
        • 1970-01-01
        • 2022-11-23
        • 1970-01-01
        • 2020-03-21
        • 2021-02-22
        相关资源
        最近更新 更多