【问题标题】:Reordering all rows of a datatable based on one of the fields根据其中一个字段重新排序数据表的所有行
【发布时间】:2015-07-26 18:00:42
【问题描述】:

我有一个包含如下列的 DataTable:

Field1 | val 1 | val 2 | val 3
Field2 | val 1 | val 2 | val 3
Field3 | val 1 | val 2 | val 3
Field4 | val 1 | val 2 | val 3

等等..一直到Field20

在绑定到我的页面上的网格之前完全重新排序行的最佳方法是什么。我会使用 Field 列来确定每一行的位置。

我做了研究,发现唯一的技术之一是克隆、删除、重新插入应该去的行。

如果我必须对所有行都执行此操作,因为没有任何行处于正确位置,是否有人有好的方法?

这是我尝试过的:

    DataTable dataTable2 = new DataTable();
    DataRow[] row = dataTable1.Select("Field ='" + "Field2" +"'");
    dataTable2 .Rows.InsertAt(row, 0);

【问题讨论】:

  • 这听起来像是一个计划,无论如何你可以看看我更新的示例并告诉我我做错了什么。

标签: c# c#-4.0 datatable


【解决方案1】:

您可以使用 LINQ 对 DataTable 进行排序。 LINQ 操作 (dt2) 的结果是 IEnumerable,但您仍然可以使用它来绑定您的网格。

var dt = new DataTable();
dt.Columns.Add("Key", typeof(string));
dt.Columns.Add("Position", typeof(int));

dt.Rows.Add("Key1Value", 2);
dt.Rows.Add("Key2Value", 3);
dt.Rows.Add("Key3Value", 4);
dt.Rows.Add("Key4Value", 5);
dt.Rows.Add("Key5Value", 0);
dt.Rows.Add("Key6Value", 1);

var dt2 = dt.AsEnumerable().OrderBy(x => x.Field<int>("Position"));
foreach (DataRow dr in dt2)
{
    Console.WriteLine("{0} {1}", dr["Key"], dr["Position"]);
}

我编辑了我的代码,使其符合您的排序顺序。我添加了一个名为 Position 的列,因此我可以随意对其进行排序。如果您想要不同的订单,只需更改 Position 的值即可。

如果不想多加一列,可以在 OrderBy 方法中使用 switch 语句。

var dt = new DataTable();
dt.Columns.Add("Key", typeof(string));

dt.Rows.Add("Key1Value");
dt.Rows.Add("Key2Value");
dt.Rows.Add("Key3Value");
dt.Rows.Add("Key4Value");
dt.Rows.Add("Key5Value");
dt.Rows.Add("Key6Value");

var dt2 = dt.AsEnumerable().OrderBy(x =>
    {
        switch (x.Field<string>("Key"))
        {
            case "Key5Value": return 0;
            case "Key6Value": return 1;
            case "Key1Value": return 2;
            case "Key2Value": return 3;
            case "Key3Value": return 4;
            case "Key4Value": return 5;
            default : return -1;
        }
    }
);

foreach (DataRow dr in dt2)
{
    Console.WriteLine(dr["Key"]);
}

基本上,OrderBy() 方法接受定义排序顺序的匿名方法。由于“Key5Value”返回 0,因此将其放在“Key6Value”之前(返回 1)。如果要将其从最大值到最小值排序,请使用 OrderByDescending() 方法。

我还编辑了您的代码。 getPosition 方法类似于我的 LINQ 代码中的 OrderBy() 方法。

using System;
using System.Data;
using System.Linq;

class Program
{
    static void Main()
    {
        var dt = new DataTable();
        dt.Columns.Add("Key", typeof(string));

        dt.Rows.Add("Key1Value");
        dt.Rows.Add("Key2Value");
        dt.Rows.Add("Key3Value");
        dt.Rows.Add("Key4Value");
        dt.Rows.Add("Key5Value");
        dt.Rows.Add("Key6Value");

        var dt2 = dt.Clone();
        foreach (DataRow dr in dt.Rows)
        {
            string key = dr["Key"].ToString();
            var dRow = dt2.NewRow();
            dRow.ItemArray = dr.ItemArray;
            dt2.Rows.InsertAt(dRow, getPosition(key));
        }

        foreach (DataRow dr in dt2.Rows)
        {
            Console.WriteLine(dr["Key"]);
        }

        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }

    static int getPosition(string key)
    {
        switch (key)
        {
            case "Key5Value":
                return 0;
            case "Key6Value":
                return 1;
            case "Key1Value":
                return 2;
            case "Key2Value":
                return 3;
            case "Key3Value":
                return 4;
            case "Key4Value":
                return 5;
            default:
                return -1;
        }
    }
}

版本 2: 我添加了一个字典,其中包含键值(例如 Key1Value)和一对值。对值是新的键值(例如键 1 值)和位置。我使用字典的原因是替换 switch-case 表达式。如果需要,您可以添加额外的价值。只需向元组添加更多类型。

Tuple<string, int, int, bool> // holds a sequence of a string, two integers and a boolean.

这是完整的代码。

using System;
using System.Data;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // create table
        var dt = new DataTable();
        dt.Columns.Add("Key", typeof(string));
        dt.Columns.Add("Val1", typeof(int));
        dt.Columns.Add("Val2", typeof(int));
        dt.Columns.Add("Val3", typeof(int));
        dt.Columns.Add("Position", typeof(int));

        // populate table
        dt.Rows.Add("Key1Value", 100, 200, 300);
        dt.Rows.Add("Key2Value", 100, 200, 300);
        dt.Rows.Add("Key3Value", 100, 200, 300);
        dt.Rows.Add("Key4Value", 100, 200, 300);
        dt.Rows.Add("Key5Value", 100, 200, 300);
        dt.Rows.Add("Key6Value", 100, 200, 300);

        // initialize dictionary
        var dict = new Dictionary<string, Tuple<string, int>>() {
            { "Key1Value", new Tuple<string, int>("Key 1 Value", 2) }, // Key1Value's new value is Key 1 Value, position is 2
            { "Key2Value", new Tuple<string, int>("Key 2 Value", 3) },
            { "Key3Value", new Tuple<string, int>("Key 3 Value", 4) },
            { "Key4Value", new Tuple<string, int>("Key 4 Value", 5) },
            { "Key5Value", new Tuple<string, int>("Key 5 Value", 0) },
            { "Key6Value", new Tuple<string, int>("Key 6 Value", 1) }
        };

        // set position and new key value
        dt.AsEnumerable()
          .ToList()
            .ForEach(x => {
                        x["Position"] = dict[x.Field<string>("Key")].Item2;
                        x["Key"] = dict[x.Field<string>("Key")].Item1; // must be the last because the dictionary key is "KeyXValue", not "Key X Value"
                     });

        // sort by Position
        var dt2 = dt.AsEnumerable().OrderBy(x => x.Field<int>("Position"));

        foreach (DataRow dr in dt2)
        {
            Console.WriteLine("{0}, {1}, {2}, {3}, {4}", dr["Key"], dr["Val1"], dr["Val2"], dr["Val3"], dr["Position"]);
        }

        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }
}

【讨论】:

  • 这似乎是一个很好的解决方案,但我不知道我是否完全理解它。它会给我充分的灵活性来将 20 行中的任何一行放在我想要的任何位置吗?我真的没有可以排序的字段。我需要说如果 Key="Field2" 在索引 0 处插入,如果 Key="Field4" 在索引 1 处插入,等等..
  • 我实施了您的第一个解决方案,因为它现在对我来说很有意义,但是我能否在返回订单之前另外更改密钥的名称?例如,我所有的键都是数据库字段名称,我想在单词之间添加空格。例如,我需要将 Key 的实际值从 Key5Value 更改为“Key 5 Value”,看来我只能使用您提供的基于我的第二种技术来做这样的事情
  • 我尝试实现第 2 版并显示“列键不属于表”。在这部分: x["Position"] = dict[x.Field("Key")].Item2;顺便说一下,我的实际 Key 列称为 CategoryName。我用那个替换了你有 Key 的所有地方,但仍然出现错误。
  • 啊,我发现了我的问题。我的源数据表中有我不想添加到网格中的行,所以我没有将它们添加到字典中。因此,我收到一条错误消息,说密钥在字典中无效。我想我不能真正使用这种方法,除非我使用数据表中的所有行。有没有办法在使用之前检查 x.Field 是否存在。基本上,假设 Key6Value 在您的数据表中,但您不希望它出现在最终结果中。您将如何避免这种情况。
  • 我只使用字典中的 2 个值,因为我只想替换表中的 2 列(键和位置)。 Val1、Val2 和 Val3 列未包含在字典中,因为我不替换这些值。
【解决方案2】:

由于时间因素,这是我可以提出的最佳解决方案。如果您觉得可以以更好的方式完成,请发表评论。

此技术涉及创建源数据表的克隆。然后循环遍历源数据表并以所需的顺序将行重新添加到 datatable2。

// Make a clone of the source datatable
DataTable dataTable2 = dataTable1.Clone();
// Loop through each row in the source datatable and check for key
foreach (DataRow dr in dataTable1.Rows)
{
    string key = dr["Key"].ToString();
    DataRow dRow = dataTable2.NewRow();
    switch (key)
    {
        case "Key5Value":
            // Rename actual key value if needed
            dr["Key"] = "Key Rename if needed";
            // Set new datarow item array to current row in loop item array
            dRow.ItemArray = dr.ItemArray;
            // Add datarow to desired position in dataTable 2
            dataTable2.Rows.InsertAt(dRow, 0);
            break;
        case "Key6Value":
            dr["Key"] = "Key Rename if needed";
            dRow.ItemArray = dr.ItemArray;
            dataTable2.Rows.InsertAt(dRow, 1);
            break;
        case "Key1Value":
            dr["Key"] = "Key Rename if needed";
            dRow.ItemArray = dr.ItemArray;
            dataTable2.Rows.InsertAt(dRow, 2);
            break;
        case "Key2Value":
            dr["Key"] = "Key Rename if needed";
            dRow.ItemArray = dr.ItemArray;
            dataTable2.Rows.InsertAt(dRow, 3);
            break;
        case "Key4Value":
            dr["Key"] = "Key Rename if needed";
            dRow.ItemArray = dr.ItemArray;
            dataTable2.Rows.InsertAt(dRow, 4);
            break;
        case "Key3Value":
            dr["Key"] = "Key Rename if needed";
            dRow.ItemArray = dr.ItemArray;
            dataTable2.Rows.InsertAt(dRow, 5);
            break;
    }
}

【讨论】:

  • 我修改了您的代码以删除一些重复的行。查看我编辑的帖子。
【解决方案3】:

另一种解决方案可能是:

  1. 在数据表中添加一个新列“IntegerKey”。
  2. 根据所需列的值更新“整数键”列的值 例如当所需的列值为“column2”时,列“IntegerKey”值 = 1 当所需的列值是“column1”时,列“IntegerKey”值 = 2 当所需的列值是“column4”时,列“IntegerKey”值 = 3
  3. datarows[] arrROWS = Datatable.Select(string.empty, "IntegerKey desc")

【讨论】:

    猜你喜欢
    • 2020-06-30
    • 1970-01-01
    • 2014-10-27
    • 1970-01-01
    • 1970-01-01
    • 2012-09-14
    • 1970-01-01
    • 2021-04-13
    • 2019-07-09
    相关资源
    最近更新 更多