如何根据 .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 列,那您就错了。相反,它将包含像 Length
、LongLength
、Rank
这样的列,...查看 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;
}
}
我有如下所示的数据表
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 列,那您就错了。相反,它将包含像 Length
、LongLength
、Rank
这样的列,...查看 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;
}
}