如何根据 .net 中的字符拆分将数据表行拆分为多行

How to split datatable row in to multiple rows based on character split in .net

我有如下所示的数据表

Dosage  Drug            Patient
-----------------------------------
  25    Indocin         David
  50    Enebrel,Crozine Sam
  10    Hydralazine     Christoff
  21    Combivent       Janet
 100    Dilantin        Melanie

根据逗号分割应该转换成如下所示

Dosage  Drug            Patient
------------------------------
  25    Indocin         David
  50    Enebrel         Sam
  50    Crozine         Sam
  10    Hydralazine     Christoff
  21    Combivent       Janet
 100    Dilantin        Melanie

我做了以下代码,但没有给出预期的结果。有人可以建议答案吗?

private static void ProcessDatatable(DataTable dt)
{
    DataTable dtnew = new DataTable();

    IEnumerable<string[]> allRowValues = dt.AsEnumerable()
                                           .Select(r => r.Field<string>(1).Split(','));

    dtnew = allRowValues.ToDataTable();
}

扩展方法:

public static DataTable ToDataTable<T>(this IEnumerable<T> collection, string tableName)
{
    DataTable tbl = ToDataTable(collection);
    tbl.TableName = tableName;

    return tbl;
}

public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
{
    DataTable dt = new DataTable();

    Type t = typeof(T);

    PropertyInfo[] pia = t.GetProperties();

    // Create the columns in the DataTable
    foreach (PropertyInfo pi in pia)
    {
        dt.Columns.Add(pi.Name, pi.PropertyType);
    }

    // Populate the table
    foreach (T item in collection)
    {
        DataRow dr = dt.NewRow();

        dr.BeginEdit();

        foreach (PropertyInfo pi in pia)
        {
             dr[pi.Name] = pi.GetValue(item, null);
        }

        dr.EndEdit();

        dt.Rows.Add(dr);
    }

    return dt;
}

你认为你想要的是:

IEnumerable<object[]> allRowValues = dataTable.AsEnumerable()
    .SelectMany(dataRow =>
        dataRow.Field<string>(1).Split(',').Select(drug => new[] { dataRow[0], drug, dataRow[2] }));

但你真正想要的是:

IEnumerable<Record> allRowValues = dataTable.AsEnumerable()
    .Select(dataRow => new Record(dataRow))
    .SelectMany(record => record.SplitDrugs());

// ...

public class Record
{
    public int Dosage { get; }
    public string Drug { get; }
    public string Patient { get; }

    public Record(int dosage, string drug, string patient)
    {
        Dosage = dosage;
        Drug = drug;
        Patient = patient;
    }

    public Record(DataRow dataRow)
        : this((int)dataRow["Dosage"], (string)dataRow["Drug"], (string)dataRow["Patient"])
    {
    }

    public IEnumerable<Record> SplitDrugs()
    {
        return Drug.Split(',').Select(drug => new Record(Dosage, drug, Patient));
    }
}

简短说明:您正试图通过花哨的 LINQ 解决世界上太多的问题,例如从数据中提取信息 table、逐行处理、应用业务逻辑并将结果合并到新的数据 table。这是编写 error-prone、不可读、untestable、unstable 和无法维护的代码的好方法。

最终会感谢您选择第二个选项的不完整人员名单:

  • 你未来的自己
  • 你的队友
  • 您的代码审查员
  • 单元测试编写器
  • 最终用户
  • 你的老师(如果有作业)
  • SO 社区

当我在做的时候,我会为您节省一些调试 allRowValues(在您的情况下是 IEnumerable<string[]> 类型)到 DataTable 的转换的时间。如果您认为它将包含 3 列,那您就错了。相反,它将包含像 LengthLongLengthRank 这样的列,...查看 properties Array class 找出原因。

编辑

OP 在另一个答案下的评论中提炼了原意。

... , but i just posted a prototype of datatable,infact actually 180 columns are there.DO i need to add all 180 columns manually in newRow.ItemArray, when there is a split of comma seperated values???Any easier way?

是的,有更简单的方法。涉及泛型,您可以将用途扩展到这个有限的用例之外:

// extension method
public static DataTable ExpandColumn<T>(this DataTable dataTable, string columnName,
    Func<T, IEnumerable<T>> expandField)
{
    var clonedDataTable = dataTable.Clone();
    var columnIndex = dataTable.Columns.IndexOf(columnName);
    var column = dataTable.Columns[columnIndex];
    foreach (var dataRow in dataTable.AsEnumerable())
    {
        var valueToExpand = dataRow.Field<T>(column);
        var expandedValues = expandField(valueToExpand);
        var originalValues = dataRow.ItemArray;
        foreach (var value in expandedValues)
        {
            originalValues[columnIndex] = value;
            clonedDataTable.Rows.Add(originalValues);
        }
    }
    return clonedDataTable;
}

// usage
var dataTableNew = dataTable.ExpandColumn<string>("Drug", drug => drug.Split(','));

上述扩展方法通过复制原始行来克隆 DataTable 实例,并通过对每个值应用 expandField 函数来扩展指定列中的值。

我还是希望你吸取我在编辑上面写的东西的教训,再三考虑你的设计。

我对 C# 不太满意,所以我不得不用老式的方式来做这件事,但它确实有效。

public partial class Form1 : Form
{
    private DataTable dt;
    private DataTable dtExpanded;

    public Form1()
    {
        InitializeComponent();
        LoadTable();
        LoadExpandedTable();
    }
    //Dosage Drug            Patient
   private void LoadTable()
    {
        dt = new DataTable();
        using (SqlConnection cn = new SqlConnection("Your connection string"))
        {
            using (SqlCommand cmd = new SqlCommand("Select * From DrugDoses", cn))
            {
                cn.Open();
                dt.Load(cmd.ExecuteReader());
            }
        }
        dataGridView1.DataSource = dt;
    }
    private void LoadExpandedTable()
    {
        dtExpanded = new DataTable();
        dtExpanded.Columns.Add("Dose");
        dtExpanded.Columns.Add("Drug");
        dtExpanded.Columns.Add("Patient");
        foreach (DataRow r in dt.Rows)
        {
            string s = (string)r["Drug"];
            if(s.Contains(","))
            {
                string[] splitName = s.Split(',');
                foreach (string drug in splitName)
                {
                    DataRow newRow = dtExpanded.NewRow();
                    newRow.ItemArray = new Object[] { r["Dosage"], drug , r["Patient"]};
                    dtExpanded.Rows.Add(newRow);
                }
            }
            else
            {
                dtExpanded.Rows.Add(r.ItemArray);
            }

        }
        dataGridView2.DataSource = dtExpanded;
    }
}