How to get selected Data Columns from Datatable? - c#

I have string array which contains some column names which may be valid or not.
I have to iterate over DataTable and return columns which are present in it.

Try this:
static public IEnumerable<DataColumn> GetColumns(DataTable dt)
{
var names = new[] { "foo", "bar" };
return dt.Columns.OfType<DataColumn>().Where(c => names.Contains(c.ColumnName));
}

public string getcolumns(DataTable dt,string[] array)
{
string columns = "";
foreach (DataColumn column in dt.Columns)
{
if(array.Contains(column.ColumnName))
{
columns += column.ColumnName + ",";
}
}
return columns;
}

Related

Remove all columns from datatable except for 25

I have 500 Columns in my DataTable and I want to remove all of them except for 25 columns.
Is there any way to do this faster to save time and lines of code?
This is what I already tried:
private static void DeleteUselessColumns()
{
//This is example data!
List<DataColumn> dataColumnsToDelete = new List<DataColumn>();
DataTable bigData = new DataTable();
bigData.Columns.Add("Harry");
bigData.Columns.Add("Konstantin");
bigData.Columns.Add("George");
bigData.Columns.Add("Gabriel");
bigData.Columns.Add("Oscar");
bigData.Columns.Add("Muhammad");
bigData.Columns.Add("Emily");
bigData.Columns.Add("Olivia");
bigData.Columns.Add("Isla");
List<string> columnsToKeep = new List<string>();
columnsToKeep.Add("Isla");
columnsToKeep.Add("Oscar");
columnsToKeep.Add("Konstantin");
columnsToKeep.Add("Gabriel");
//This is the code i want to optimize------
foreach (DataColumn column in bigData.Columns)
{
bool keepColumn = false;
foreach (string s in columnsToKeep)
{
if (column.ColumnName.Equals(s))
{
keepColumn = true;
}
}
if (!keepColumn)
{
dataColumnsToDelete.Add(column);
}
}
foreach(DataColumn dataColumn in dataColumnsToDelete)
{
bigData.Columns.Remove(dataColumn);
}
//------------------------
}
var columnsToKeep = new List<string>() { "Isla", "Oscar", "Konstantin", "Gabriel"};
var toRemove = new List<DataColumn>();
foreach(DataColumn column in bigData.Columns)
{
if (!columnsToKeep.Any(name => column.ColumnName == name ))
{
toRemove.Add(column);
}
}
toRemove.ForEach(col => bigData.Columns.Remove(col));
Test1...test9 same code could be made a loop. No need to add the columns to delete in a list, just delete them in the first while loop. As for performance, not sure how to improve it.
You could try to use a DataView that selects the desired columns then copy to table. You need to experiment.
if they have different names create an array of string
var columns = new string[] { "Harry", "Konstantin","John"};
var columnsToKeep = new string[] { "John", "Konstantin"};
var columnsToDelete = from item in columns
where !columnsToKeep.Contains(item)
select item;
or using lambda
var columnsToDelete = columns
.Where (i=> !columnsToKeep.Contains(i))
.ToList();
toDelete
Harry

Datatable from List<Row>

I have downloaded data that is contained in a List<Row> Rows like this:
class Row
{
string[] Items { get; set; }
public Row(string[] Items)
{
this.Items = Items;
}
}
The rows are basically comma delimited entries (.csv)
using (var reader = new StreamReader(spreadSheetStream))
{
string header = reader.ReadLine(); //This is the header
Rows.Add(new Row(header.Split(',')));
while (!reader.EndOfStream)
{
string tickerInfo = reader.ReadLine(); //This is a data entry
Rows.Add(new Row(tickerInfo.Split(',')));
}
}
I convert the List<Row> into a Datatable like this
DataTable historicalDataTable = ToDataTable<Row>(Rows);
The first element of List<Row> Rows contains the names of the columns, seven of them. Then each element thereafter is an actual data element.
public static DataTable ToDataTable<T>(List<T> items)
{
DataTable dataTable = new DataTable(typeof(T).Name);
//Get all the properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Setting column names as Property names
dataTable.Columns.Add(prop.Name);
}
foreach (T item in items)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//inserting property values to datatable rows
values[i] = Props[i].GetValue(item, null);
}
dataTable.Rows.Add(values);
}
//put a breakpoint here and check datatable
return dataTable;
}
When I try to write out the contents of the table, I see the right number of rows, but there is nothing in ItemArray
foreach (DataRow dataRow in historicalDataTable.Rows)
{
Console.WriteLine(dataRow.ToString());
foreach (var item in dataRow.ItemArray)
{
Console.WriteLine(item);
}
}
Your code is a bit contradictory. You are trying to copy properties as column names, however your csv code actually populates the first row as the column names. You have no distinction between header rows and data rows
You can just read it straight into a datatable with something like :-
(though you may want to do better error checking)
var dt = new DataTable("Rows");
string data = "a,b,c\r\n1,2,3\r\n4,5,6";
var stream = GenerateStreamFromString(data); // http://stackoverflow.com/questions/1879395/how-to-generate-a-stream-from-a-string
using (var reader = new StreamReader(stream))
{
reader.ReadLine()?.Split(',').ToList().ForEach(h => dt.Columns.Add(h));
while (!reader.EndOfStream)
{
dt.Rows.Add(reader.ReadLine()?.Split(',').ToArray());
}
}
foreach (DataColumn dataColumn in dt.Columns)
{
Console.Write($"{dataColumn.ColumnName} ");
}
Console.WriteLine();
foreach (DataRow dataRow in dt.Rows)
{
Console.Write("Row: ");
foreach (var item in dataRow.ItemArray)
{
Console.Write(item + " ");
}
Console.WriteLine();
}

Loop through DataTable and select only certain values in column

I have a DataTable from which I would like to loop through each row and column and then select a value from a specific column depending on the other values in the columns/each row.
My code currently looks like this:
foreach (DataRow drow in dt.Rows)
{
foreach (DataColumn dcol in dt.Columns)
{
foreach (var Item in ImportData)
{
if (Item.Value.Equals(true))
{
if (Item.Key.Equals("" + dcol))
{
string value = drow[dcol].ToString();
if (value.Equals("X"))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
}
}
}
}
ImportData is a Dictionary<string, bool>, which holds the data that I want to compare with my DataTable.
string outDraws is just a string which should hold the content of the drawings I want to print out.
My problem now is that I only want to print out the content in the column 'Drawing' of the row where all columns with the same name as the Keys in ImportData have 'X' as value. At the moment I'm getting all the rows where any of the columns have 'X' as value and has the same name as any Key in ImportData.
I understand that it will be quite hard for you to get what I want to do but please ask if you need any more information and I will try to provide.
Many thanks in advance.
Edit:
ImportData contains the name of different products as keys. These products have either been selected or not by the customer through another program, if they have been selected they have the value true and if not selected they have the value false.
With the method presented above I would like to compare ALL the keys that have the value true with the column names in the DataTable. If the column name corresponds to the key in ImportData (which is the name of a product) then I want to check if that column in a specific row has 'X' as value.
This goes on for ALL the keys in ImportData and in the end I should know which row in the DataTable that has an 'X' in all the columns with the same name as the keys in ImportData. For this row I would like to get the content of the column called 'Drawing'.
So for an example say that ImportData contains:
[Motor, true][Product6, true][Product7, true]
Then I would like to print out the column Drawing at row 6.
Unfortunately I can't post pictures..
As with any problem: divide and conquer. Break down your problem in smaller pieces and go from there.
From what I understand, you want to do something with certain rows from the datatable. Something like:
foreach (var drow in dt.Rows.OfType<DataRow>())
{
if (SomeConditionIsMet(dt, drow, ImportData))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
The function SomeConditionIsMetcould looks like this:
private static bool SomeConditionIsMet(
DataTable dt, DataRow drow,
IDictionary<string, bool> importData)
{
// TODO if the condition is met, return true
// otherwise, return false
}
Now your problem is simplified to thinking about what it means that 'Some condition is met'. Once you can clearly express that in words, rename the function to reflect that (e.g. to 'AllColumnsAreChecked')
Here's a sample with solution as I understand it:
internal class Program
{
private static void Main(string[] args)
{
var importData = new Dictionary<string, bool>()
{
{"Product1", true},
{"Product2", false},
{"Product3", true},
};
var dt = new DataTable();
dt.Columns.Add("Product1");
dt.Columns.Add("Product2");
dt.Columns.Add("Product3");
dt.Columns.Add("Product4");
dt.Columns.Add("Drawing");
// row1 should be added
var row1 = dt.NewRow();
row1["Product1"] = "X";
row1["Product3"] = "X";
row1["Drawing"] = "Drawing1";
dt.Rows.Add(row1);
// row2 should not be added
var row2 = dt.NewRow();
row2["Product1"] = "X";
row2["Drawing"] = "Drawing2";
dt.Rows.Add(row2);
string outDraws = string.Empty;
foreach (DataRow drow in dt.Rows.OfType<DataRow>())
{
if (AllColumnsAreChecked(drow, importData))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
Console.WriteLine(outDraws);
}
private static bool AllColumnsAreChecked(DataRow drow, Dictionary<string, bool> importData)
{
foreach (var key in importData.Keys)
{
if (!importData[key])
continue;
var value = drow[key] as string;
if (value != "X")
return false;
}
}
}
Bonus: here's a LINQ based implementation of the check:
private static bool AllColumnsAreChecked(DataRow drow, Dictionary<string, bool> importData)
{
return importData.Keys
.Where(k => importData.ContainsKey(k) && importData[k]) // the field must be enabled in importData
.All(k => (drow[k] as string) == "X"); // the corresponding value in the row must be 'X'
}
Try this
DataTable tbl = new DataTable();
foreach (DataRow row in tbl.Rows)
{
object cellData = row["colName"];
}

Should I Convert excel to csv or write htmltable to csv

I have 10 reports in my application which I let users to export to excel. I have never written CSV files. In my existing application, I convert the results from the stored procedure to an HTML table and write it to Excel. Some of my results from the stored procedures have dynamic columns so I use dapper. My new requirement is to provide CSV export as well.
So should I first convert html datatable to excel and convert it to CSV or write HTML datatable to CSV. I dont want to manually parse because there are 10 different reports with different columns and some of the reports have dynamic columns so I cant manually parse.
Stored procs returning Dapper, Dynamic Columns
EFDbContext db = new EFDbContext();
var recordDate = StartDate.Date;
var cnn = new SqlConnection(db.Database.Connection.ConnectionString);
cnn.Open();
var p = new DynamicParameters();
p.Add("#StartDate", StartDate);
p.Add("#UserRoleID", UserRoleID);
p.Add("#SelectedSystemIDs", SelectedSystemIDs);
p.Add("#SelectedPartIDs", SelectedPartIDs);
p.Add("#SelectedSubSystems", SelectedSubsystems);
p.Add("#SelectedServiceTypes", SelectedServiceTypes);
var obs = cnn.Query(sql: "spExportInstrumentConfigAll", param: p, commandType: CommandType.StoredProcedure);
var dt = ToDataTable(obs);
return ExportDatatableToHtml(dt);
public static DataTable ToDataTable(IEnumerable<dynamic> items)
{
if (items == null) return null;
var data = items.ToArray();
if (data.Length == 0) return null;
var dt = new DataTable();
foreach (var pair in ((IDictionary<string, object>)data[0]))
{
dt.Columns.Add(pair.Key, (pair.Value ?? string.Empty).GetType());
}
foreach (var d in data)
{
dt.Rows.Add(((IDictionary<string, object>)d).Values.ToArray());
}
return dt;
}
public static string ExportDatatableToHtml(DataTable dt)
{
StringBuilder strHTMLBuilder = new StringBuilder();
strHTMLBuilder.Append("<html >");
strHTMLBuilder.Append("<head>");
strHTMLBuilder.Append("</head>");
strHTMLBuilder.Append("<body>");
strHTMLBuilder.Append("<table border='1px' cellpadding='1' cellspacing='1 style='font-family:Garamond; font-size:medium'>");
strHTMLBuilder.Append("<tr >");
foreach (DataColumn myColumn in dt.Columns)
{
strHTMLBuilder.Append("<td >");
strHTMLBuilder.Append(myColumn.ColumnName);
strHTMLBuilder.Append("</td>");
}
strHTMLBuilder.Append("</tr>");
foreach (DataRow myRow in dt.Rows)
{
strHTMLBuilder.Append("<tr >");
foreach (DataColumn myColumn in dt.Columns)
{
strHTMLBuilder.Append("<td >");
strHTMLBuilder.Append(myRow[myColumn.ColumnName].ToString());
strHTMLBuilder.Append("</td>");
}
strHTMLBuilder.Append("</tr>");
}
//Close tags.
strHTMLBuilder.Append("</table>");
strHTMLBuilder.Append("</body>");
strHTMLBuilder.Append("</html>");
string Htmltext = strHTMLBuilder.ToString();
return Htmltext;
}
Non-Dynamic Columns mapped to entity
return db.Database.SqlQuery<ServiceEntryPartExportDataRow>("[dbo].[spExportServiceParts] #parm1, #parm2, #parm3, #parm4,#parm5,#parm6",
new SqlParameter("parm1", StartDate),
new SqlParameter("parm2", EndDate),
new SqlParameter("parm3", Reconciled),
new SqlParameter("parm4", ServiceTypes),
new SqlParameter("parm5", SelectedSystemIDs),
new SqlParameter("parm6", UserRoleID)
).ToList().ToHTMLTable();
public static string ToHTMLTable<T>(this IList<T> data)
{
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
StringBuilder builder = new StringBuilder();
builder.Append("<table border=\"1\">");
builder.Append("<tr>");
for (int i = 0; i < props.Count; i++)
{
builder.Append("<td>");
PropertyDescriptor prop = props[i];
builder.Append(prop.Name);
builder.Append("</td>");
}
builder.Append("</tr>");
object[] values = new object[props.Count];
foreach (T item in data)
{
builder.Append("<tr>");
for (int i = 0; i < values.Length; i++)
{
builder.Append("<td>");
builder.Append(props[i].GetValue(item));
builder.Append("</td>");
}
builder.Append("</tr>");
}
builder.Append("</table>");
return "<html><body>" + builder.ToString() + "</body></html";
}
Current code Sending to Excel
return new PostActionResult(htmlTable, "ServiceEntryHistory", submit);
public PostActionResult(string htmlTable, string typeName, string submit) { this.htmlTable = htmlTable; this.typeName = typeName; this.submit = submit; }
public PostActionResult(DataTable dataTable, string typeName, string submit) { this.dataTable = dataTable; this.typeName = typeName; this.submit = submit; }
public override void ExecuteResult(ControllerContext context)
{
if (submit == "Excel")
{
ExcelHelpers.ExportToExcel(context.HttpContext, typeName, htmlTable);
}
if (submit == "CSV")
{
ExcelHelpers.ExportToExcelCSV(context.HttpContext, typeName, htmlTable);
}
}
public static void ExportToExcel(HttpContextBase httpBase, string fileNamePrefix, string table)
{
string TimeStamp = DateTime.Now.ToLocalTime().ToString();
string fileName = string.Format("attachment;filename={0}_{1}.xls", fileNamePrefix, TimeStamp);
httpBase.Response.ClearHeaders();
httpBase.Response.ClearContent();
httpBase.Response.Clear();
httpBase.Response.AddHeader("content-disposition", fileName);
httpBase.Response.ContentType = "application/vnd.ms-excel";
httpBase.Response.Write(table);
httpBase.Response.End();
}
You already have code to build an HTML table from the data. Building a CSV is very nearly identical. For brevity, let's simplify the HTML table pseudo-code:
builder.Append("<table>");
// header
builder.Append("<tr>");
foreach (var column in columns)
builder.Append("<th>" + column.name + "</th>");
builder.Append("</tr>");
// rows
foreach (var row in rows)
{
builder.Append("<tr>");
foreach (var column in row.columns)
builder.Append("<td>" + column.value + "</td>");
builder.Append("</tr>");
}
builder.Append("</table>");
Building a CSV is the exact same structure:
// header
foreach (var column in columns)
builder.Append("\"" + column.name + "\",");
// there's now an extra comma at the end. remove it, or use a
// different method to have built the row, such as string.Join.
// rows
foreach (var row in rows)
{
foreach (var column in row.columns)
builder.Append("\"" + column.value + "\",");
// there's now an extra comma at the end. remove it, or use a
// different method to have built the row, such as string.Join.
builder.Append(Environment.NewLine);
}
Remember that this is free-hand pseudo-code, there are some clean-ups you can employ. You might also check the column types to determine if you need those escaped quotes or not, since numeric types wouldn't want them. But the point is that the structure is the same. A CSV is text in the same way that HTML is text. It's only the dressing around the values that's different.
Side note: This is actually a classic example of the Template Method Pattern.

Datatable subset of columns from another datatable

I have a datatable with 17 columns and a bunch of data.
I wnat a datatable with only 6 of the columns and the data for those 6 columns.
So I need a subset of the original datatable.
How do I loop through the original datatable with 17 columns and end up with a datatable with only the 6 columns I want with the corresponding data for those 6 columns?
Private Function createSmallCopyofExistingTable(ByVal SourceTable As DataTable) As DataTable
Dim newTable As DataTable = New DataTable()
'Copy Only 6 columns from the datatable
Dim ColumnsToExport() As String = {"ID", "FirstName", "LastName", "DateOfBirth", "City", "State"}
newTable = SourceTable.DefaultView.ToTable("tempTableName", False, ColumnsToExport)
Return newTable
End Function
Without knowing more about how generic this needs to be its really just...
foreach (DataRow dr in dt.Rows)
{
newDt.Rows.Add(dr["col1"],dr["col5"],etc);
}
what about data types, and columns? are these same? if yes, you can create
object[] row = new object[]{// Fill your rows manually};
before filling it create
DataTable dt = new DataTable();
dt.Columns.Add("Title",typeof(string etc..));.....
and finally
dt.Rows.Add(row);
Personally, I would avoid creating another instance of a DataTable.
It depends on your situation, of course, but if this is purely for usability and not for security (i.e. you're not trying to remove columns with sensitive data before transmitting it somewhere), then I would create a wrapper object that encapsulates the columns that you want to expose.
The benefit of using a wrapper is in case you are doing any updates, then you can update the source table directly rather than the copy. Whether this really matters, of course, depends on your situation.
A simple example with limited functionality:
public class MyFormOrPage
{
void UsageExample()
{
DataTable allDataTable = new DataTable();
// populate the data table with whatever logic ...
// wrap the data table to expose only the Name, Address, and PhoneNumber columns
var limitedDataTable = new DataTableWrapper(allDataTable, "Name", "Address", "PhoneNumber");
// iterate over the rows
foreach (var limitedDataRow in limitedDataTable)
{
// iterate over the columns
for (int i = 0; i < limitedDataTable.ColumnCount; i++)
{
object value = limitedDataRow[i];
// do something with the value ...
}
}
// bind the wrapper to a control
MyGridControl.DataSource = limitedDataTable;
}
}
public class DataTableWrapper : IEnumerable<DataRowWrapper>
{
private DataTable _Table;
private string[] _ColumnNames;
public DataTableWrapper(DataTable table, params string[] columnNames)
{
this._Table = table;
this._ColumnNames = columnNames;
}
public int ColumnCount
{
get { return this._ColumnNames.Length; }
}
public IEnumerator<DataRowWrapper> GetEnumerator()
{
foreach (DataRow row in this._Table.Rows)
{
yield return new DataRowWrapper(row, this._ColumnNames);
}
}
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
// if you _really_ want to make a copy of the DataTable, you can use this method
public DataTable CopyToDataTable()
{
DataTable copyTable = new DataTable();
for (int index = 0; index < this._ColumnNames.Length; index++)
{
DataColumn column = this._Table.Columns[index];
copyTable.Columns.Add(column);
}
foreach (DataRow row in this._Table.Rows)
{
DataRow copyRow = copyTable.NewRow();
for (int index = 0; index < this._ColumnNames.Length; index++)
{
copyRow[index] = row[this._ColumnNames[index]];
}
copyTable.Rows.Add(copyRow);
}
return copyTable;
}
}
// let's make this a struct, since potentially very many of these will be instantiated
public struct DataRowWrapper
{
private DataRow _Row;
private string[] _ColumnNames;
public DataRowWrapper(DataRow row, params string[] columnNames)
{
this._Row = row;
this._ColumnNames = columnNames;
}
// use this to retrieve column values from a row
public object this[int index]
{
get { return this._Row[this._ColumnNames[index]]; }
set { this._Row[this._ColumnNames[index]] = value; }
}
// just in case this is still needed...
public object this[string columnName]
{
get { return this._Row[columnName]; }
set { this._Row[columnName] = value; }
}
}

Categories

Resources