I have a DataSet which I get a DataTable from that I am being passed back from a function call. It has 15-20 columns, however I only want 10 columns of the data.
Is there a way to remove those columns that I don't want, copy the DataTable to another that has only the columns defined that I want or is it just better to iterate the collection and just use the columns I need.
I need to write the values out to a fixed length data file.
Aside from limiting the columns selected to reduce bandwidth and memory:
DataTable t;
t.Columns.Remove("columnName");
t.Columns.RemoveAt(columnIndex);
To remove all columns after the one you want, below code should work. It will remove at index 10 (remember Columns are 0 based), until the Column count is 10 or less.
DataTable dt;
int desiredSize = 10;
while (dt.Columns.Count > desiredSize)
{
dt.Columns.RemoveAt(desiredSize);
}
The question has already been marked as answered, But I guess the question states that the person wants to remove multiple columns from a DataTable.
So for that, here is what I did, when I came across the same problem.
string[] ColumnsToBeDeleted = { "col1", "col2", "col3", "col4" };
foreach (string ColName in ColumnsToBeDeleted)
{
if (dt.Columns.Contains(ColName))
dt.Columns.Remove(ColName);
}
How about you just select the columns you want like this:
Dim Subjects As String = "Math, English"
Dim SubjectData As DataTable = Table.AsDataView.ToTable(True, Subjects.Split(","))
Related
I have this code :
string strdata = Encoding.Unicode.GetString(buffer);
char[] splitchar = new Char[] { '\x00' };
string[] assetdata = strdata.Split(splitchar, strdata.Length);
Buffer is a text data which goes as one row and consists of 4 types of variables. You can see example of Encoded in Unicode buffer following this link :
http://pastebin.com/ScdGX8it
So there are 4 types of data here which needs to be filled into DataGridView rows separated by 4 columns , so it can me sorted and manipulated after. Assetdata is array with this data separated by each value as a single element , but i need to group them - that is the main problem.
Thanks.
Here's one way without LINQ, that uses a datatable as the datasource for the datagridview.
DataTable dt = new DataTable("T1");
dt.Columns.AddRange(new DataColumn[] { new DataColumn("A"), new DataColumn("B"), new DataColumn("C"), new DataColumn("D")});
for (int i = 0; i < assetdata.Length; i += 4)
{
dt.Rows.Add(new string[]{assetdata[i],assetdata[i+1],assetdata[i+2],assetdata[i+3]});
}
dataGridView1.DataSource = dt;
This way you can modify the datatable and update the datagridview, which will probably give you more options since this fits in more with how the datagridviewwas was designed.
LINQ is your friend. Go through the tutorials on grouping. It should be pretty easy, since you already have a string[]. Once you're done with your query, call the ToIEnumerable() extension method to bind to a DataGridView.
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
I've found this piece of code that can be used to get all distinct values. But my datatable has 10 columns. The distinctValues only shows the columns I write in the toTable(); Is it possible to use this function, but also show the rest of the columns?
DataView view = new DataView(table);
DataTable distinctValues = view.ToTable(true, "Column1", "Column2");
Unless those columns you mention are the full key to the table, there is no guarantee that for a particular combination of those two columns the other columns will have exactly one value.
And if they were the key, then there would be no need to use a "distinct" filter.
You can use Linq-To-DataTable
var distinct = from row in table.AsEnumerable()
group row by new
{
Col1 = row.Field<string>("Column1"),
Col2 = row.Field<string>("Column2")
} into Group
select Group.First()
DataTable tblDistinct = distinctRows.CopyToDataTable();
(assuming that you just want an arbitrary row[the first])
In my project there are two datatables dtFail and dtFailed (dtFailed has nothing but column names declarations). dtFail has duplicate "EmployeeName" column values. so i took a dataview dvFail and did the process to make them distinct as shown in the below code:
dtFail
I tried the below code:
DataView dvFail = new DataView(dtFail);
dtFail = dvFail.ToTable(true, "EmployeeName"); //showing only one column in dtFail
dtFailed (only one column)
If i do like below
DataView dvFail = new DataView(dtFail);
dtFail = dvFail.ToTable(true, "EmployeeName","EmployeeRole","Status");
dtFailed (showing but with duplicate rows)
Then the datatable dtFailed is storing duplicate "EmployeeName" also.
Please Help
Thanks in Advance.
Try this query-
DataTable distinctTable = originalTable.DefaultView.ToTable( /*distinct*/ true);
For more info hit below link-
https://social.msdn.microsoft.com/Forums/en-US/ed9c6a6a-a93e-4bf5-a892-d8471b84aa3b/distinct-in-datatable-or-dataview?forum=adodotnetdataset
I hope this would have helped you.
SOLUTION 1:
Based on the question my understanding is, we need to consider duplicates based on EmployeeName and we need not worry about other columns. If that is the case below solution works better.
foreach(DataRow r in dtFail.AsEnumerable())
{
if (!dt1.AsEnumerable().Any(r1 => r1["EmployeeName"] == r["EmployeeName"]))
{
// if you don't want to copy entire row create new DataRow
// with required fields and add that row.
dt1.Rows.Add(r.ItemArray);
}
}
if you want you can put dt1 back to dtFail.
SOLUTION 2:
If we need to consider distinct rows I prefer below solution.
var temp = dtFail.AsEnumerable().Distinct();
dtFail = temp.CopyToDataTable();
I'm not sure it will be helpful or not. As far as I get from your question that you want EmployeeName to be distinct irrelevant to other columns. But if you do ToTable and turn on the distinct flag it will give all the distinct rows, doesn't matter how many columns are involved there. So if you mention only EmployeeName it will obviously give you distinct EmployeeNames, not all the columns associated with it.
So, thats what I did, initially select only the distinct EmployeeName columns and put it into a temp DataTable dtt.
DataTable dtt = dvFail.DefaultView.ToTable(true, "EmployeeName");
Secondly I've created another temp DataTable where we put the segregated rows from the main DataTable dtFail and set the column names manually.
DataTable TempDataTable = new DataTable();
DataTable dtFailed = new DataTable();
Prepare the columns in the dtFailed DataTable.
if (dtFailed.Columns.Count == 0)
{
dtFailed.Columns.Add("EmployeeName");
dtFailed.Columns.Add("EmployeeRole");
dtFailed.Columns.Add("Status");
dtFailed.Columns.Add("Date");
}
Loop through the distinct EmployeeName dtt DataTable and match the EmployeeName and keep that selected first row in the TempDataTable. Finally all rows transferred into the dtFailed.
for (int j = 0; j < dtt.Rows.Count; j++)
{
string EmployeeName = dtt.Rows[j]["EmployeeName"].ToString();
TempDataTable = dvFail.Select("EmployeeName = " + EmployeeName).CopyToDataTable();
dtFailed.Rows.Add(TempDataTable.Rows[0].ItemArray);
}
I have now a problem with a very old system of ours. (!It is more then 7 years old and I have no budget and resources to make bigger change in the structure, so the decision to improve the old logic as many as we can.!)
We have an own written gridcontrol. Basically it is like a normal ASP.NET grid, you can add, change, delete elements.
The problem is that the grid has a BindGrid() method, where for further usage, the rows of the datasource table copied into a DataRow[]. I need to keep the DataRow[], but I would like to implement the best way to copy the source from the the table into the array.
The current solution:
DataRow[] rows = DataSource.Select("1=1", SortOrderString);
As I experienced so far, if I need to get a specified sort, that could be the best way (I'm also interested if it has a quicker way or not.)
BUT there are some simplified pages, where the SortOrder is not needed.
So I could make two method one for the sort order and one for without.
The real problem is the second one:
DataRow[] rows = DataSource.Select("1=1");
Because it is very slow. I made some test and it is kind of 15 times slower then the CopyTo() solution:
DataRow[] rows = new DataRow[DataSource.Rows.Count];
DataSource.Rows.CopyTo(rows,0);
I would like to use the faster way, BUT when I made the tests some old function simply crashed. It seems, there is an other difference, what I only noticed now:
The Select() gets the rows like the RowChanges are accepted.
So if I deleted a row, and I do not use the AcceptRowChanges() (I can't do that unfortunately), then with Select("1=1") the row is in the DataSource but not in the DataRow[].
With a simple .CopyTo() the row is there, and that is a bad news for me.
My questions are:
1) Is the Select("1=1") the best way to get the rows by the RowChanges? (I doubt a bit, because it is like 6 year old part)
2) And if 1) is not, is it possible to achieve a faster way with the same result than the .Select("1=1") ?
UPDATE:
Here is a very basic test app, what I used for speedtesting:
DataTable dt = new DataTable("Test");
dt.Columns.Add("Id", typeof (int));
dt.Columns.Add("Name", typeof(string));
for (int i = 0; i < 10000; i++)
{
DataRow row = dt.NewRow();
row["ID"] = i;
row["Name"] = "Name" + i;
dt.Rows.Add(row);
}
dt.AcceptChanges();
DateTime start = DateTime.Now;
DataRow[] rows = dt.Select();
/*DataRow[] rows = new DataRow[dt.Rows.Count];
dt.Rows.CopyTo(rows,0);*/
Console.WriteLine(DateTime.Now - start);
You can call Select without an argument: DataRow[] allRows = DataSource.Select(); That would be for sure more efficient than "1=1" since that applies a pointless RowFilter.
Another way is using Linq-To-DataSet to order and filter the DataTable. That isn't more efficient but more readable and maintainable.
I have yet no example or measurement, but it is obvious that a RowFilter with "1=1" is more expensive than none. Select is implemented in this way:
public Select(DataTable table, string filterExpression, string sort, DataViewRowState recordStates)
{
this.table = table;
this.IndexFields = table.ParseSortString(sort);
this.indexDesc = Select.ConvertIndexFieldtoIndexDesc(this.IndexFields);
// following would be omitted if you would use DataSource.Select() without "1=1"
if (filterExpression != null && filterExpression.Length > 0)
{
this.rowFilter = new DataExpression(this.table, filterExpression);
this.expression = this.rowFilter.ExpressionNode;
}
this.recordStates = recordStates;
}
If you want to be able to select also the rows that are currently not accepted, you can use the overload of Select:
DataRow[] allRows = DataSource.Select("", "", DataViewRowState.CurrentRows | DataViewRowState.Deleted);
This will select all rows inclusive the rows that are deleted even if AcceptChanges was not called yet.
i have a table with a column named size which will have values big,medium and small. wat i want is i want to sort this column in such a way that all rows with size big should come first, then rows with size medium and finally rows with size small.
is there a way by which i can achieve this?
edit: it is a data table which i add to a dataset.
edit: wat if the initials of the words, in this case b,m and s, are not in alphabetical order. in that wat am i supposed to do coz i have another application as well where i'll be required to sort a column having value High, normal and low.
Example
DataTable dt = ds.Tables[0];
DataView dv = new DataView(dt);
dv.Sort = sortExpression + direction; //sortexpression will be fieldname direction will be ascending descending
GridView1.DataSource = dv;
GridView1.DataBind()
Have a look at this article: Express Yourself with Expression-based Columns
http://msdn.microsoft.com/en-us/library/ms810291.aspx
OF COURSE IT WORKS!
DataSet myDs = new DataSet();
DataTable myDt = new DataTable("Table1");
myDs.Tables.Add(myDt);
myDt.Columns.Add("Size", typeof(string));
myDt.Columns.Add("Ranking", typeof(int), "Iif((Size)='Large', 1, Iif((Size)='Medium', 2, Iif((Size)='Small', 3, 4)))");
DataRow myDr1 = myDs.Tables["Table1"].NewRow();
DataRow myDr2 = myDs.Tables["Table1"].NewRow();
DataRow myDr3 = myDs.Tables["Table1"].NewRow();
DataRow myDr4 = myDs.Tables["Table1"].NewRow();
myDr1["Size"] = "Large";
myDr2["Size"] = "Medium";
myDr3["Size"] = "Small";
myDr4["Size"] = "Large";
myDt.Rows.Add(myDr1);
myDt.Rows.Add(myDr2);
myDt.Rows.Add(myDr3);
myDt.Rows.Add(myDr4);
DataView myDv = new DataView(myDt);
myDv.Sort = "Ranking";
ultraGrid1.DataSource = myDv;
And that is the code that proves it.
Map big, medium, small to an enum in the correct order and sort on that.
Any other answer will require more information as asked for in the comments.
If this is really as masqueraded T-SQL question, you could either use UNION (as proposed by Lukasz), or you could use a few WHEN ... THEN constructs in your ORDER BY:
SELECT *
FROM SomeTable
ORDER BY WHEN size = 'big' THEN 2 WHEN 'medium' THEN 1 ELSE 0 END
This might also work in ADO.NET, but I have not tested it:
myDataTable.DefaultView.Sort =
"WHEN size = 'big' THEN 2 WHEN 'medium' THEN 1 ELSE 0 END";
EDIT: David points out, that in your specific case (big, medium, small), the alphabetical sort order matches the expected ordering. In this special case, you can simply do:
ORDER BY size
Or in ADO.NET:
myDataTable.DefaultView.Sort = "size";
If you want a simple solution (and if these values are unlikely to change), just order by the 'size' column, since the big, medium and small words are in alphabetical order :D
Using the built-in sorting algorithm of the DataView (which is where you'd do actual sorting, not in the DataTable itself), this isn't possible, strictly speaking; there is no support for "custom" sorting, which is what you'd need. The sorting capabilities of the DataView are actually surprisingly limited, and (IIRC) it isn't even a stable sort (since it uses Array.Sort, which is documented as unstable).
Your only real option (from a GUI perspective) is to use a data display control that provides custom sorting capability, like the DevExpress XtraGrid.
After edits by OP, it has become (somewhat) clear that we are dealing with a DataTable.
One approach to sorting the rows of a DataTable according to a "mapped value", would be to use its Select method to retrieve several arrays (one for each value) of rows:
List<DataRow> rows = new List<DataRow>();
rows.AddRange(oldTable.Select("size = 'Big'"));
rows.AddRange(oldTable.Select("size = 'Medium'"));
rows.AddRange(oldTable.Select("size = 'Small'"));
If needed, the rows can be imported into a fresh DataTable with the same schema:
DataTable newTable = oldTable.Clone();
foreach (DataRow row in rows)
{
newTable.ImportRow(row);
}
This approach is definitely not very efficient, but it's probably about the easiest way to do this.
Try a switch statement.
Switch(var)
{ Case "Big":
//Append to TextBox1
//Or add to var1[]
break;
Case "Small":
//Append to TextBox2
//Or add to var2[]
break;
Case "Medium":
//Append to TextBox3
//Or add to var3[]
break;
}
EDIT: Sort into txtbox's or var[]'s, does this go-round make more sense?