使用多个排序条件对 System.Data.DataTable 进行排序

Posted

技术标签:

【中文标题】使用多个排序条件对 System.Data.DataTable 进行排序【英文标题】:Sort System.Data.DataTable with multiple sort criteria 【发布时间】:2020-08-02 18:20:59 【问题描述】:

我有这个System.Data.DataTable

DataTable dt = new DataTable();
dt.Columns.Add("Sector", typeof(string));
dt.Columns.Add("BBG IPC", typeof(double));
dt.Columns.Add("Analyst", typeof(string));
dt.Columns.Add("Issuer Group", typeof(string));
dt.Columns.Add("Seniority", typeof(string));
dt.Columns.Add("Mkt Value", typeof(double));
dt.Columns.Add("Nom Value", typeof(double));
dt.Columns.Add("Issue Group Rating", typeof(string));
dt.Columns.Add("Current SBR", typeof(string));
dt.Columns.Add("Notches", typeof(int));
dt.Columns.Add("Forward SBR", typeof(string));
dt.Columns.Add("Probability of Downgrade", typeof(string));
dt.Columns.Add("Risk Category", typeof(string));
dt.Columns.Add("Comment", typeof(string));
dt.Columns.Add("Restricted Message", typeof(string));


dt.Rows.Add(new object[]  "Agencies", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "BB",-1, "YY", "Hight", "R1", "Comment", "gvbhjnnijnibj" );
dt.Rows.Add(new object[]  "Agencies", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "CC",2, "II", "Low", "R2", "Bergtrgrt", "Other" );
dt.Rows.Add(new object[]  "Agencies", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "EE",3, "LL", "Mid", "R1", "vggvbhjjnnjoioion", "ggvbhibniujbuhvg uvvugvghV" );
dt.Rows.Add(new object[]  "Consumer", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "OO",-1, "SS", "Higth", "R3", "vgvgyhvgvjhbkj", "bibhjbhjbjhb  ubuyhbuyhb hbuhbuhbhb" );
dt.Rows.Add(new object[]  "Energy", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "PP",-2, "QQ","Higth", "R1", "ertyuiop", "tfcgvhb Uvugvugvuhvh" );
dt.Rows.Add(new object[]  "Energy", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "GG",-3, "FF", "Low", "R2", "gvgvgvvgfccfdxdxrtf xrtfvgh tdctfgv trcfygvh tcfyghv b ygvhb ", "ygvgubiujb" );

我需要根据这些规则对表格进行排序:

第一级:按字母顺序排列的扇区。

第二级:降级概率按以下顺序:高、中、低

第 3 级:按字母顺序排列的发行人组

我尝试过类似这样的dt.DefaultView.Sort("Sector", "Probability of Downgrade", "Issuer Group");,但不起作用。我该怎么做?谢谢!

【问题讨论】:

使用DataTable 是否有特定原因,而不是某种列表?通常它带来的痛苦多于收获,但当然也可能有原因:-) 您尝试过版本:DataView dv = dt.DefaultView; dv.Sort = "[Sector], [Probability of Downgrade], [Issuer Group]";,然后检查dv 变量吗? 为了解决第二个排序要求(高、中、低),您能否根据此列分配一个额外的数值? 【参考方案1】:

DataView.Sort 属性的语法与 SQL Server 中使用的语法非常相似。

对于您的示例,您可以使用以下字符串:

dt.DefaultView.Sort = "Sector ASC, [Probability of Downgrade] ASC, [Issuer Group] ASC";

.. 然后只需在您的代码中访问 dt.DefaultView

但您可能会遇到问题,因为(“High”、“Mid”、“Low”)的升序排序会将它们返回为“High”、“Low”、“Mid”。因为它们是字符串,所以它会按字母顺序进行。

如果源数据库中有概率,最简单的解决方案是使用概率的数字表示,并显示高/中/低但不按它们排序。

这是一个 LINQ 解决方法,如果您拥有的唯一数据选项是概率字符串:

// Make a dictionary to map your probability strings to integers.
var probLookup = new Dictionary<string, int>();
probLookup["High"] = 3;
probLookup["Mid"] = 2;
probLookup["Low"] = 1; 

// Convert your DataTable into an array of a new type, that has the DataRow
// plus the three things you need to sort by, as separate properties.
var temp = dt.Select().Select(row => new()    
                                   Row = row,
                                   Sector = row["Sector"],
                                   // Map the downgrade prob. to an integer.
                                   DowngradeProb = probLookup[(string) row["Probability of Downgrade"]],
                                   IssuerGroup = row["Issuer Group"]
                                 );

// Now, sort it by the 3 properties we created.
var temp2 = temp
           .OrderBy(a => a.Sector)
           .ThenByDesc(a => a.DowngradeProb)
           .ThenBy(a => a.IssuerGroup);

// Now, fish out the Row again.
var temp3 = temp2.Select(a => a.Row).ToArray();

重要免责声明: LINQ 是凭记忆完成的:我不保证!

【讨论】:

谢谢你,安!但我不太了解如何解决“高、低、中”问题。 我的 LINQ 代码应该会处理它,但我同意这有点难以理解!我会重写一下。 好的,已更新。我将其更改为使用 Probability of Downgrade 字符串作为字典的键,该字典具有所需顺序的数值。然后我按 Sector、数值IssuerGroup 对结果进行排序。最后,我创建了一个DataRow 对象数组,并将其分配给变量temp3,您可以对它做任何您需要做的事情。【参考方案2】:

尝试以下与您上次请求类似的方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication11

    class Program
    
        static void Main(string[] args)
        
            List<string> probabilityStr = new List<string>()  "Hight", "Mid", "Low" ;

            DataTable dt = new DataTable();
            dt.Columns.Add("Sector", typeof(string));  //1
            dt.Columns.Add("BBG IPC", typeof(double));
            dt.Columns.Add("Analyst", typeof(string));
            dt.Columns.Add("Issuer Group", typeof(string));  //3
            dt.Columns.Add("Seniority", typeof(string));
            dt.Columns.Add("Mkt Value", typeof(double));
            dt.Columns.Add("Nom Value", typeof(double));
            dt.Columns.Add("Issue Group Rating", typeof(string));
            dt.Columns.Add("Current SBR", typeof(string));
            dt.Columns.Add("Notches", typeof(int));
            dt.Columns.Add("Forward SBR", typeof(string));
            dt.Columns.Add("Probability of Downgrade", typeof(string));  //2
            dt.Columns.Add("Risk Category", typeof(string));
            dt.Columns.Add("Comment", typeof(string));
            dt.Columns.Add("Restricted Message", typeof(string));


            dt.Rows.Add(new object[]  "Agencies", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "BB", -1, "YY", "Hight", "R1", "Comment", "gvbhjnnijnibj" );
            dt.Rows.Add(new object[]  "Agencies", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "CC", 2, "II", "Low", "R2", "Bergtrgrt", "Other" );
            dt.Rows.Add(new object[]  "Agencies", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "EE", 3, "LL", "Mid", "R1", "vggvbhjjnnjoioion", "ggvbhibniujbuhvg uvvugvghV" );
            dt.Rows.Add(new object[]  "Consumer", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "OO", -1, "SS", "Higth", "R3", "vgvgyhvgvjhbkj", "bibhjbhjbjhb  ubuyhbuyhb hbuhbuhbhb" );
            dt.Rows.Add(new object[]  "Energy", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "PP", -2, "QQ", "Higth", "R1", "ertyuiop", "tfcgvhb Uvugvugvuhvh" );
            dt.Rows.Add(new object[]  "Energy", 180969, "MUSTO", "Caisse des Depots", "Senior", 10114481, 1000000, "AA", "GG", -3, "FF", "Low", "R2", "gvgvgvvgfccfdxdxrtf xrtfvgh tdctfgv trcfygvh tcfyghv b ygvhb ", "ygvgubiujb" );


            DataTable dt2 = dt.Clone();

            var sectors = dt.AsEnumerable()
                .OrderBy(x => x.Field<string>("Sector"))
                .ThenBy(x => probabilityStr.IndexOf(x.Field<string>("Probability of Downgrade")))
                .ThenBy(x => x.Field<string>("Issuer Group"))
                .GroupBy(x => x.Field<string>("Sector"))
                .ToList();

            for (int si = 0; si < sectors.Count(); si++)
            
                var probabilities = sectors[si].GroupBy(x => x.Field<string>("Probability of Downgrade")).ToList();
                for (int pi = 0; pi < probabilities.Count(); pi++)
                
                    var groups = probabilities[pi].GroupBy(x => x.Field<string>("Issuer Group")).ToList();
                    for (int gr = 0; gr < groups.Count(); gr++)
                    
                        for (int r = 0; r < groups[gr].Count(); r++)
                        
                            DataRow row = dt2.Rows.Add();
                            for (int i = 0; i < dt2.Columns.Count; i++)
                            
                                switch (dt2.Columns[i].ColumnName)
                                
                                    case "Sector" :
                                        if (pi == 0)
                                        
                                            row["Sector"] = sectors[si].Key;
                                        
                                        else
                                        
                                            row["Sector"] = DBNull.Value;
                                        
                                        break;

                                    case "Probability of Downgrade" :
                                        if (gr == 0)
                                        
                                            row["Probability of Downgrade"] = probabilities[pi].Key;
                                        
                                        else
                                        
                                            row["Probability of Downgrade"] = DBNull.Value;
                                        
                                        break;

                                    case "Issuer Group" :
                                        if (r == 0)
                                        
                                            row["Issuer Group"] = groups[gr].Key;
                                        
                                        else
                                        
                                            row["Issuer Group"] = DBNull.Value;
                                        
                                        break;

                                    default :
                                        row[i] = groups[r].First()[dt2.Columns[i].ColumnName];
                                        break;
                                

                            
                        
                    
                
            
        
    

【讨论】:

有必要像你在“dt2”中那样改变列的顺序吗?因为在这种情况下,我需要保持与“dt”中完全相同的列顺序和结构。谢谢 我假设您首先想要分组列,就像您的屏幕截图一样。 但是在这段代码之后,扇区只在第一次出现时出现。我需要它出现在所有行中,因为稍后我需要按扇区进行其他提取。我试图删除 row["Sector"] = DBNull.Value;但它是一样的。我该怎么办? 去掉 If 语句,只剩下 row["Sector"] = sector[si].Key;

以上是关于使用多个排序条件对 System.Data.DataTable 进行排序的主要内容,如果未能解决你的问题,请参考以下文章

在 Grails 中使用条件对多个字段进行排序

是否可以使用归并排序 (C#) 基于多个条件对数组进行排序?

如何使用异常条件对多个字段进行排序

如何按值和键的多个条件对嵌套字典进行排序?

按多个条件对向量进行排序

为多个条件实现按逻辑排序以对最终结果进行排序的问题