I have this string:
1#3.doc#0.036/n
2#1.doc#0.026/n
I want to split it on # and put every line in a single row inside DataTable
like this:
1 3.doc 0.036
2 1.doc 0.026
I have a DataTable like this:
DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("FileName", typeof(string));
table.Columns.Add("Content", typeof(string));
How can I do that?
Here is how you would split a string into lines, and then those lines into different parts.
Your string is first split by the new line character \n into an array of lines string[].
Then those lines, one by one, are split into parts by Split('#').
And finally those parts are added to your table with the columns you created.
Remember to save the columns you created and don't forget to add the newly created row to the table.
DataTable table = new DataTable();
DataColumn colID = table.Columns.Add("Id", typeof(int));
DataColumn colFileName = table.Columns.Add("FileName", typeof(string));
DataColumn colContent = table.Columns.Add("Content", typeof(string));
string source = "1#3.doc#0.036\n2#1.doc#0.026\n";
string[] lines = source.Split('\n');
foreach(var line in lines)
{
string[] split = line.Split('#');
DataRow row = table.NewRow();
row.SetField(colID, int.Parse(split[0]));
row.SetField(colFileName, split[1]);
row.SetField(colContent, split[2]);
table.Rows.Add(row);
}
Adding data to the row with row["FileName"] = data is also possible, but this will break if you change the name of your column, while references to the column objects are checked by the compiler and your IDE. Also this article explains how to create a typed DataTable, which is something you may want to do.
erm,
var stuff = someString.Split('\n')
.Select(r => r.Split('#')
.Select(a => new
{
Id = int.Parse(a[0]),
FileName = a[1],
Content = a[2]
})
.ToList();
This will give you an IList of an anonymous type. Its not worth putting it in a DataTable.
Related
I have a stored procedure that returns extra columns. I don't have control of the stored procedure. I would like to use the following form to generate my worksheet:
ws.Cells.LoadFromDataTable(dt, true, OfficeOpenXml.Table.TableStyles.Light8)
How can I output just the columns I want?
I tried to figure out a Linq query but the problem is that the column names are inaccessible so I can't specify which columns I would like.
SqlConnection cx = new SqlConnection(util.GetConnectionString());
SqlCommand cmd = new SqlCommand("StoredRept", cx);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter ta = new SqlDataAdapter();
ta.SelectCommand = cmd;
DataTable dt = new DataTable();
ta.Fill(dt);
FileInfo newFile = new FileInfo("c:\temp");
ExcelPackage epp = new ExcelPackage(newFile);
var ws = epp.Workbook.Worksheets.Add("WS");
// here is where I would like to copy certain columns off into another data table but I haven't been able to figure out how
ws.Cells.LoadFromDataTable(dt, true, OfficeOpenXml.Table.TableStyles.Light8);
Any help would be greatly appreciated.
If removing some columns from the table is the only problem, that is easy to resolve. Try something like this:
var dt = new DataTable();
dt.Columns.Add("First", typeof(string));
dt.Columns.Add("Second", typeof(string));
dt.Columns.Add("Third", typeof(string));
dt.Columns.Add("Fourth", typeof(string));
dt.Columns.Add("Fifth", typeof(string));
for (var i = 1; i < 6; i++)
{
dt.Rows.Add($"First {i}", $"Second {i}", $"Third {i}",$"Fourth {i}",$"Fifth {i}");
}
//dt.Dump();//If you have linqpad this is handy to dump table to output
//REMOVE THE COLUMNS.
dt.Columns.RemoveAt(1);
dt.Columns.RemoveAt(2);
//dt.Dump();//again in linqpad this dumps table with remaining 3 columns
You can use following method to find column by name and remove it:
var col=dt.Columns["Second"];
dt.Columns.Remove(col);
Here is linq query to get list with desired columns.
var lq = (from DataRow r in dt.Rows
select new { First = r[0], Second=r[1], Third=r["Fifth"]}
).ToList();
lq.Dump();
Note how you can use both column index or name to get value from row object.
Can you load your spreadsheet with IEnumerable<T> instead of DataTable? If so:
var someColumns = dt
.AsEnumerable()
.Select(row => new
{
When = row.Field<DateTime>("When"),
What = row.Field<string>("What"),
HowMany = row.Field<int>("ColumnNameInDatabase"),
});
If you need DataTable, you can convert IEnumerable to it via any method, like this or this.
BUT
Frankly, I answered your problem, but IMO you've a greater problem at hand, which is using those antiquated DataTable objects instead of IEnumerable<T>. If you consider, for example, Dapper library, you could do this:
util.GetConnectionString()
.Query("StoredRept", commandType: CommandType.StoredProcedure) // Extension method from Dapper
.Select(dyn => new
{
When = (DateTime)dyn.When,
What = (string)dyn.What,
HowMany = (int)dyn.ColumnNameInDatabase
})
I see you're using EPPlus library for Excel manipulation. It can load IEnumerable<T> data, you are not restricted to DataTable.
I have Json stored in DataBase which I deserialize into DataTable with the help of Newtonsoft.Json like this
string jsonString = "[myJsonfromDB....]";
//Deserialize to DataTable
DataTable dtSerialized = (DataTable)JsonConvert.DeserializeObject(jsonString, (typeof(DataTable)));
Which gives me result like this other columns in image are not shown
Here my label is Column and value is column value. Both of these columns will be moved to new DataTable which I'll process further for my operations. Now my problem is that I want to do it in one loop while I do it in multiple loops i.e add columns first (in first loop) and then add column values (in second loop). Currently I'm doing it like this
string colName = string.Empty;
// First Loop to add columns
foreach (DataRow dr in dtSerialized.Rows)
{
if (!string.IsNullOrEmpty(Utility.Instance.ToString(dr["label"])))
{
colName = prefix + "_" + Utility.Instance.ToString(dr["label"]).Replace(" ", string.Empty).Replace("/", "_").Replace("-", "_");
if (!dtResult.Columns.Contains(colName))
dtResult.Columns.Add(colName, typeof(string));
}
}
DataRow drSelect = dtResult.NewRow();
//Second loop to add column values
foreach (DataRow dr in dtSerialized.Rows)
{
if (!string.IsNullOrEmpty(Utility.Instance.ToString(dr["label"])))
{
colName = prefix + "_" + Utility.Instance.ToString(dr["label"]).Replace(" ", "").Replace("/", "_").Replace("-", "_");
drSelect[colName] = dr["value"];
}
}
dtResult.Rows.Add(drSelect);
dsResult.Tables.Add(dtResult);
After this I have
As much I know is that first DataRow schema is built from DataTable and then values can be added which is clear in above code. Now, How can i do it in one loop? Or should I search for alternate method which i don't know how to do this.
Thanks in advance
I am guessing I am missing something here. This looks like a transpose function and I cannot think of a way to accomplish this without two loops or transposing the data as you read it in. But going from what is posted it appears the column label holds the new DataTable’s column names. The first column is the first row of data to this new DataTable.
If this is the case then while you are looping through the rows to get the column names from column 1 (label), you can also get the “value’ from column 0 (value) and put this value in a List<string> named valuesList below.
Then after you have looped through all the rows and set the columns in the new DataTable dtResults you can add a single row from the valuesList by setting the list to a string array like below. This will produce the second picture you showed in one loop. Again I am guessing there is more to it than this simple transpose. Since a DataTable does not have a built in transpose function you will have to write your own. Not sure how you would do this in one loop though. Hope this helps.
private DataTable Transpose2ColDT(DataTable dtSource) {
string prefix = "DIAP_";
string colName = "";
DataTable dtResult = new DataTable();
List<string> valuesList = new List<String>();
if (dtSource.Rows.Count > 0) {
foreach (DataRow dr in dtSource.Rows) {
if (!dr.IsNull("Label")) {
if (dr.ItemArray[1].ToString() != "" ) {
colName = prefix + "_" + dr.ItemArray[1].ToString();
if (!dtResult.Columns.Contains(colName)) {
dtResult.Columns.Add(colName, typeof(string));
valuesList.Add(dr.ItemArray[0].ToString());
}
}
}
}
dtResult.Rows.Add(valuesList.ToArray<string>());
} // no rows in the original source
return dtResult;
}
How can I copy 1 data column from 1 data table to a new datatable. When I try to do it, I get the error Column 'XXX' already belongs to another DataTable.?
dataColumn = datatable1.Columns[1];
datatable2 = new DataTable();
datatable2.Columns.Add(dataColumn);
Thanks in Advance
You cannot copy DataColumns. What you'll need to do is create a new DataColumn in the new datatable with the same data type as in the old datatable's column, and then you need to run a FOR loop to bring in all the data from the old datatable to the new datatable.
See the following code. This assumes that the datatables have exactly the same number of rows.
DataTable dt1 = new DataTable();
DataTable dt2 = new DataTable();
dt2.Columns.Add("ColumnA", dt1.Columns["ColumnA"].DataType);
for (int i = 0; i < dt1.Rows.Count; i++)
{
dt2.Rows[i]["ColumnA"] = dt1.Rows[i]["ColumnA"];
}
Also, If the data you are copying are reference types and not value types you might want to see if a .Clone() method is available for the type, or make one yourself. Just doing 'this = that' in the FOR loop will not work on reference types.
You cannot copy a DataColumn. (DataColumns are very tightly coupled with their tables)
Instead, you can add a new column with the same name and datatype.
You might be looking for DataTable.Clone(), which will create a structual copy of an entire table. (With the same schema, but no data)
Just a thought, are your DataTables both in the same DataSet?
If so, you can create a named DataRelation between the columns of two tables (think foreign key).
Then you can add a Calculated DataColumn to your table that has its Expression property set to "Child(RelationName).ColumnName" or "Parent(RelationName).ColumnName" depending on the direction of the relationship.
This will give you the same effect as copying the column, but I believe it only evaluates it lazily. So maybe it will give you what you need.
There is an example here of how this works. The example uses the Sum aggregate function, but you just need to reference the column name and it will duplicate it in your DataTable
myDataSet.Relations.Add(
"Orders2OrderLines",
myDataSet.Tables["Orders"].Columns["OrderID"],
myDataSet.Tables["OrderLines"].Columns["OrderID"]);
ordersTable.Columns.Add("OrderTotal", typeof(decimal), "Sum(Child(Orders2OrderLines).ExtendedPrice)");
HTH
The problem is caused by the c# can not reuse the object instance created and uses it on multiples DataTables. For this it is necessary to create a new object DataCollumn for each loop iteration.
foreach (DataTable table in DATASET.Tables)
{
DataColumn yourDataCollumn = new DataColumn("Name of DataCollumn", typeof(Your data type));
// your logic here
}
Hope it's help...
I used the below to merge two tables using mostly LINQ and only looping through the rows at the end. I wouldn't call it pretty but it does work. Using the join to prevent some of the assumptions listed above.
DataTable tableOne = getTableOne();
DataTable tableTwo = getTableTwo();
var oneColumns = tableOne.Columns.Cast<DataColumn>()
.Select(p => new Column(p.ColumnName, DataType))
.ToArray();
var twoColumns = tableTwo.Columns.Cast<DataColumn>()
.Select(p => new DataColumn(p.ColumnName, p.DataType))
.ToArray();
var matches = (from a in tableOne.AsEnumerable()
join b in tableTwo.AsEnumerable() on a["column_name"] equals b["column_name"]
select a.ItemArray.Concat(b.ItemArray)).ToArray();
DataTable merged = new DataTable();
merged.Columns.AddRange(oneColumns);
merged.Columns.AddRange(twoColumns);
foreach (var m in matches) { merged.Rows.Add(m.ToArray()); }
No looping required , Refer this , Hope this should solve your problem...
DataTable dt = new DataTable();
//fill the dt here
DataTable dt2 = new DataTable();
string[] strCols = {"Column Name to copy"};
dt2 = dt.DefaultView.ToTable("newTableName", false, strCols);
I am looking at how to split a string and store the info in a datatable. I can get the split and store to work correctly but the issue comes in how I am trying to use the split. this is an example of the string I have:
itemid/n3,itemid/n4
itemid is the items unique id and after /n is how many of the item the user has selected, the comma seperates the entries
I have a data table like this:
DataTable table = new DataTable();
table.Columns.Add("id", typeof(int));
table.Columns.Add("count", typeof(int));
Id like to be able to split the string at the comma and then store each of the values in the data table so they appear on the same row (split at the /n) is there an easy way to do this using split? or am I better off doing it another way
Yeah, you may split by the comma first and by /n afterwards:
foreach(var row in myString.Split(','))
{
var fields = row.Split(new string[] { "/n" },
StringSplitOptions.None);
// fields[0] is ID, fields[1] is count
}
This still executes in linear time, therefore it may definitely be a way to go.
If "/n" and "," are always present for each record, you can use a regular expression split with the expression "(?:/n|\,)" and then loop with x+=2 instead of x++ through the list. X will be the ID, X+1 will be the value.
string Input = "12/nTwelve,13/nThirteen,";
string[] InputSplit = Regex.Split(Input, #"(?:/n|\,)");
for(int i = 0 ; i < ((InputSplit.Length / 2) * 2) ; i+=2){
//Math in the middle helps when there's a trailing comma in the data set
Console.WriteLine(string.Format("{0}\t{1}", InputSplit[i], InputSplit[i+1]));
}
Note that for the example, I changed the type of the first column, as in the provided sample string, id is a string.
DataTable table = new DataTable();
table.Columns.Add("id", typeof(string));
table.Columns.Add("count", typeof(int));
var str = "itemid/n3,itemid/n4";
var items =
str.Split(',').Select(
r =>
new
{
Id = r.Split(new[] {"/n"}, StringSplitOptions.RemoveEmptyEntries).First(),
Count = int.Parse(r.Split(new[] {"/n"}, StringSplitOptions.RemoveEmptyEntries).Last())
});
foreach (var item in items)
{
var row = table.NewRow();
row["id"] = item.Id;
row["count"] = item.Count;
}
Is there anyway to store row header information in a datatable so that when i bind it to a datagridview, it will automatically display both the column and row headers in c#?
Linqpad Demo-Program
As far as i understood you would like to add the column name as values into the datatable / the datagridview. The following is a Linqpad-Program you can easily copy paste into Linqpad to play around. The code adds the column-names to the first row to the datatable. You can easily bind this datatable to a gridview - but beware that each column of the datatable must be of type string.
void Main()
{
GetDataTable().Dump();
}
public DataTable GetDataTable()
{
var dt = new DataTable();
dt.Columns.Add("Id", typeof(string)); // dt.Columns.Add("Id", typeof(int));
dt.Columns["Id"].Caption ="my id";
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Job", typeof(string));
dt.Rows.Add(GetHeaders(dt));
dt.Rows.Add(1, "Janeway", "Captain");
dt.Rows.Add(2, "Seven Of Nine", "nobody knows");
dt.Rows.Add(3, "Doctor", "Medical Officer");
return dt;
}
public DataRow GetHeaders(DataTable dt)
{
DataRow dataRow = dt.NewRow();
string[] columnNames = dt.Columns.Cast<DataColumn>()
.Select(x => x.ColumnName)
.ToArray();
columnNames.Dump();
dataRow.ItemArray = columnNames;
return dataRow;
}
Update 2019-06 with additional explanation and alternative code
The method GetHeaders is not the simplest option to get the headers.
Previoulsy the extension method Cast<TResult>(IEnumerable) was used on the DataColumnCollection-Class An alternative would be to just iterate over the collection - this what is done In GetHeadersNew T
public DataRow GetHeadersNew(DataTable dt)
{
DataRow row = dt.NewRow();
DataColumnCollection columns = dt.Columns;
for (int i = 0 ;i <columns.Count ;i++)
{
row[i] = columns[i].ColumnName;
}
return row;
}
This is likely more efficient because less objects and methods are involved.
As long as you can create them with the code based on the data in the row I would just add them at run time using c#. Add a column to the datatable and run through it with a foreach loop. As long as there are not too many rows this code will execute very quickly:
DataTable dt = new DataTable();
// code here to get your datatable
dt.Columns.Add("rowheader");
foreach (DataRow r in dt.Rows)
{
r["rowheader"] = "my nice row header";
}
Then output the new column rowheader as the first cell in the grid.
Another solution is to use the sql query to return an 'extra' column in the result set. for example:
Select *, 'my nice row header' as rowheader from myTable
In this way you make SQL do all the work.