Better way to filter DataTable - c#

I currently have a DataTable with the following columns: Date, X1, Y1, Z1, X2, Y2, Z2... Xn, Yn, Zn.
When populated, Date ALWAYS has a value, and X/Y/Z1 to X/Y/Zn can be DBNull, a string, or an int. If the entire row with the exception of Date, is DBNull, i would like to remove that particular row.
I am currently doing an exhaustive search, looping through each row with a for loop, and then with a nested for loop, checking each cell, if i do not find any data (ie. only dbnull's), i then call RemoveAt, and reset the outer loop to start at zero again.
Is there a better/less hacky way of performing this operation? The initial building of the datatable cannot be modified, this must be something that happens post building.

If I understand correctly, you want to remove a row if all columns has DbNull.Value.
Try the following to do that.
DataTable table = new DataTable();
string[] columns = table.Columns.Cast<DataColumn>()
.Select(x => x.ColumnName)
.Skip(1)//skip to ignore first column
.ToArray();
Method1:
Remove all invalid rows
var invalidRows = table.AsEnumerable()
.Where(x => columns.All(c => x.Field<object>(c) == DBNull.Value))
.ToArray();
foreach (var row in invalidRows)
{
table.Rows.Remove(row);
}
Method2: take only valid rows and make new DataTable as suggested my #Tim in comments to improve performance when you have many invalid rows
var newTable = table.AsEnumerable()
.Where(x => columns.Any(c => x.Field<object>(c) != DBNull.Value))
.CopyToDataTable();

ATTENTION : THESE ARE MY EXAMPLES>>NOT EXCATLLY FOR YOUR TABLE>>>SO CHANGE IT FOR YOURSELF
The Main Help is Here >> Help
And Then
Way one :
dtData.Select("ID=1 AND ID2=3");
Way two :
GridFieldDAO dao = new GridFieldDAO();
//Load My DataTable
DataTable dt = dao.getDT();
//Get My rows based off selection criteria
DataRow[] drs = dt.Select("(detailID = 1) AND (detailTypeID = 2)");
//make a new "results" datatable via clone to keep structure
DataTable dt2 = dt.Clone();
//Import the Rows
foreach (DataRow d in drs)
{
dt2.ImportRow(d);
}
//Bind to my new DataTable and it will only show rows based off selection
//criteria
myGrid.DataSource = dt2;
myGrid.DataBind();
And The best Way is :
DataTable tblFiltered = table.AsEnumerable()
.Where(row => row.Field<String>("Nachname") == username
&& row.Field<String>("Ort") == location)
.OrderByDescending(row => row.Field<String>("Nachname"))
.CopyToDataTable();

May be this will help you. Try this
var ordered = yourdatatable.AsEnumerable().Where(x => x.Field<DateTime>("ColumnName") != null);
if (ordered.Count() > 0)
{
yourdatatable= orderedCopyToDataTable();
}
you can do the same for other columns as well.
Or
Why don't you check for the null values in your query. check for ISNULL(columnName, value) As ColumnName. Check more details here

You can use this little Linq query:
var columnsWithoutDate = table.Columns.Cast<DataColumn>().Skip(1);
table = table.AsEnumerable()
.Where(row => columnsWithoutDate.Any(col => !row.IsNull(col)))
.CopyToDataTable();
Skip(1) returns all columns but the first, so your date column is excluded. The Where enumerates all DataRows in the table and takes all rows with at least one non-null field(see:DataRow.IsNull(column)). Finally CopyToDataTable creates a new DataTable.

I would go for something like this:
var test = from row in table.AsEnumerable()
where (!row.IsNull("col1") || !row.IsNull("col2"))
select row;
//option1
DataTable dt = test.CopyToDataTable<DataRow>();
//option2
DataTable dt2 = new DataTable();
dt2.Columns.Add("col1", typeof(String));
dt2.Columns.Add("col2", typeof(Int32));
foreach (var v in test)
{
DataRow dr = dt2.NewRow();
dr["col1"] = v.Field<String>("col1");
dr["col1"] = v.Field<Int32>("col2");
dt2.Rows.Add(dr);
}

Did you try using RowFilter of DataTable?
DataTable dt = GetData();
//set the filter
dt.DefaultView.RowFilter = "----your filter----";
//then access the DataView
foreach (DataRowView drv in dt.DefaultView)
{
//you can also get a row from rowview
DataRow dr = drv.Row;
}
Check this documentation, they also explain how to handle null values in filters.
http://msdn.microsoft.com/en-us/library/system.data.dataview.rowfilter.aspx
You can also use Select() method with same filter, refer the below answer there is a good comparison on both approach.
DataView.RowFilter Vs DataTable.Select() vs DataTable.Rows.Find()
I would not suggest using AsEnumerable() approach, though looks like simple code but it is just like doing a foreach loop on rows and having IF conditions.
DataTable filter approach should be faster than AsEnumerable() (I am not sure, but I am assuming this because DataTable is .net's powerful data structure to handle tabular data)

modified answer:
myDataTable.AsEnumerable().Where(a => a.ItemArray.Count(b=>b != DBNull.Value)==1).ToList().ForEach(row => dataTable.Rows.Remove(row));
I checked, it works.
EDIT:
in response to #Tim Schmelter comment:
1 . you need myDataTable.AsEnumerable() in C#
If you have a strongly typed DataTable, you do not. I assumed it's the case, since OP says:
The initial building of the datatable cannot be modified, this must be
something that happens post building.
Maybe I did't understand what he meant (my English sometimes fails me)
2 . count the non-null fields is incorrect since a string can be null
which is not the same as if it is DBNull.Value(also according OP's
specifications)
You are probably right. If OP says he only wants DBNull, the second condition should be removed (it's a bad habit of mine to check for null just in case)
3 . ToList creates another List which is redundant
Yes. And if there's no ToList(), ForEach() can't be used. The old fashioned foreach can be used instead, or beter for loop (since foreach doesn't like when you try to modify collection inside it). Still you have to keep your result in some way.
4 . DataRow.Delete does it not remove from the table what is desired,
but it flags it as deleted for a DataAdapter(OP's has not mentioned
that he's using one, it is also not desired).
Thank you for pointing that out.

Related

Delete a specific row from datatable

I am trying to delete a specific row from datatable. When adding the last row, I need to delete the yellow colored rows. It is very easy to Select from a datatable like below
DataRow[] dr = dt.Select("STOK_KODU='HAMMADDE_2'");
I was wondering if there is a way like below to delete ??? Or would you advice an easy way to delete a rows from datatable?
dt.Delete("STOK_KODU='HAMMADDE_2'");
One way is to recreate the table with the rows you want to keep:
dt = dt.AsEnumerable()
.Where(row => row.Field<string>("STOK_KODU") != "HAMMADDE_2")
.CopyToDataTable()
The other is to use DataRowCollection.Remove:
DataRow[] rowsToRemove = dt.Select("STOK_KODU='HAMMADDE_2'");
foreach (var rowToDelete in rowsToRemove)
dt.Rows.Remove(rowToDelete);
The second approach is more efficient if you want to delete few rows and the table is large. The first approach using LINQ is more powerful since you can use any code but it can be less efficient.
You can access the Rows collection of the DataTable:
foreach (var row in dr)
dt.Rows.Remove(row);
try this ::
DataRow[] rows;
rows=dt.Select("STOK_KODU='HAMMADDE_2'");
foreach(DataRow r in rows)
r.Delete();
Deleting rows from an in memory DataTable object is really easy
dt.Select("STOK_KODU='HAMMADDE_2'").AsEnumerable().ToList().ForEach(x => x.Delete());
However you should consider that the Delete method simply marks the RowState to Deleted, but the rows are still in the DataTable.Rows collection. To really remove them you need to call
dt.AcceptChanges();
without this call, if you loop over the datatable rows collection, you need to check the RowState to avoid an error message stating that you cannot access the information of a deleted row
foreach(DataRow r in dt.Rows)
{
if(r.RowState != DataRowState.Deleted)
Console.WriteLine(r[0].ToString());
}

Linq on DataTable: select specific column into datatable, not whole table

I'm running a LINQ query on a datatable in c#.
I want to select specific columns rather than the whole row and enter the result into a datatable. How can i do that??
My Code:
public DataTable getConversions(string c_to, string p_to)
{
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == c_to &&
r.Field<string>("p_to") == p_to
select r;
DataTable conversions = query.CopyToDataTable();
If you already know beforehand how many columns your new DataTable would have, you can do something like this:
DataTable matrix = ... // get matrix values from db
DataTable newDataTable = new DataTable();
newDataTable.Columns.Add("c_to", typeof(string));
newDataTable.Columns.Add("p_to", typeof(string));
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == "foo" &&
r.Field<string>("p_to") == "bar"
let objectArray = new object[]
{
r.Field<string>("c_to"), r.Field<string>("p_to")
}
select objectArray;
foreach (var array in query)
{
newDataTable.Rows.Add(array);
}
Try Access DataTable easiest way which can help you for getting perfect idea for accessing DataTable, DataSet using Linq...
Consider following example, suppose we have DataTable like below.
DataTable ObjDt = new DataTable("List");
ObjDt.Columns.Add("WorkName", typeof(string));
ObjDt.Columns.Add("Price", typeof(decimal));
ObjDt.Columns.Add("Area", typeof(string));
ObjDt.Columns.Add("Quantity",typeof(int));
ObjDt.Columns.Add("Breath",typeof(decimal));
ObjDt.Columns.Add("Length",typeof(decimal));
Here above is the code for DatTable, here we assume that there are some data are available in this DataTable, and we have to bind Grid view of particular by processing some data as shown below.
Area | Quantity | Breath | Length | Price = Quantity * breath *Length
Than we have to fire following query which will give us exact result as we want.
var data = ObjDt.AsEnumerable().Select
(r => new
{
Area = r.Field<string>("Area"),
Que = r.Field<int>("Quantity"),
Breath = r.Field<decimal>("Breath"),
Length = r.Field<decimal>("Length"),
totLen = r.Field<int>("Quantity") * (r.Field<decimal>("Breath") * r.Field<decimal>("Length"))
}).ToList();
We just have to assign this data variable as Data Source.
By using this simple Linq query we can get all our accepts, and also we can perform all other LINQ queries with this…
Here I get only three specific columns from mainDataTable and use the filter
DataTable checkedParams = mainDataTable.Select("checked = true").CopyToDataTable()
.DefaultView.ToTable(false, "lagerID", "reservePeriod", "discount");
LINQ is very effective and easy to use on Lists rather than DataTable. I can see the above answers have a loop(for, foreach), which I will not prefer.
So the best thing to select a perticular column from a DataTable is just use a DataView to filter the column and use it as you want.
Find it here how to do this.
DataView dtView = new DataView(dtYourDataTable);
DataTable dtTableWithOneColumn= dtView .ToTable(true, "ColumnA");
Now the DataTable dtTableWithOneColumn contains only one column(ColumnA).
Your select statement is returning a sequence of anonymous type , not a sequence of DataRows. CopyToDataTable() is only available on IEnumerable<T> where T is or derives from DataRow. You can select r the row object to call CopyToDataTable on it.
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == c_to &&
r.Field<string>("p_to") == p_to
select r;
DataTable conversions = query.CopyToDataTable();
You can also implement CopyToDataTable Where the Generic Type T Is Not a DataRow.

Filter DataTable Efficiently

What's the most efficient way of filtering DataRows in a DataTable? I have a list of integers and want to retrieve all rows (and eventually create a DataTable from them) which match the integers in the list. I'm currently using the code below, but it's quite slow. Am I missing a more efficient way?
foreach (var i in integerlist)
{
DataRow dr = (from row in originalDataTable.AsEnumerable()
where row.Field<int>("urlID") == i
select row).FirstOrDefault<DataRow>();
if (dr!= null)
{
newDataTable.Rows.Add(dr);
}
}
I suggest you to try to do vice versa.
foreach (var row in originalDataTable)
{
if(integerList.Contains( (int)row["urlID"]))
newDataTable.ImportRow(row)
}
It makes even more sense if you have more rows in dataset then integers in your int collection.
Hope it helps :)
Hm... may be I'm missing something, but...
Woudn't be it easier just use DataView and apply a RowFilter for it ?
you could try doing a join such as:
var resultSet =
from row in originalDataTable.AsEnumerable()
join i in integerlist
on row.Field<int>("urlID") equals i
select row;
that should give you the full result set.
if you need a datatable you could do:
resultSet.CopyToDataTable();
As #Tigran says you can use the dataview, check this msdn article on how to accomplish just that.
Basically you use a DataView to filter the data and the you call the DataView.ToTable method to get the new DataTable.

How to remove rows from huge data table without iterating it?

I have a DataTable available with me which contains thousands of rows. There is a column called EmpID which is containing '0' for some of the rows. I want to remove them from my current DataTable and want to create a new correct DataTable. I cannot go row by row checking it since it contains huge amount of data. Give me a suggestion to overcome this problem.
the best way would be to filter it at source (if possible) - so if you are creating it from a db, exclude all 0 values in your sql query itself using a where
starting .net 2.0, ms enhanced the filtering logic on the datatable to a great extent. so if you used the dataview (on top of your datatable) and added the where clause in there and added some sort of runtime indexes on this field, it would give you the desired results without looping over all records
You can use DataTable.Select("EmpID <> 0"). This will return an array of DataRows which you can create your new DataTable from if required.
Isn't it possible to first select the rows with EmpID = 0 and then iterate over these only ?
DataTable newTable = new DataTable();
foreach (DataRow dr in oldTable.Select("EmpID = '0'")) {
newTable.Rows.Add(dr);
oldTable.Rows.Remove(dr);
}
You can try
DataRow[] temp=
table.Select("EmpID ='0'");
foreach(DataRow dr in temp)
{
table.Rows.Remove(dr);
}
table.acceptchanges();

How to search User Input within DataTable Column

Currently I'm searching as below.
DataRow[] rows =
dataTable.Select("FieldName='"+
userInput + "'");
The problem here is whenever user provides an input with single quote ('), it throws error.
I can easily correct it by
DataRow[] rows =
dataTable.Select("FieldName='" +
userInput.Replace("'","''") + "'");
I'm worried what other user inputs might cause problem?
Here is the exact answer from honourable Mr. Jon Skeet.
#Ismail: It would be a good habit if we validate user input before using that in front end query or in back-end query.
So i think in your scenario you must have function like...
if(ValidateInput(userInput))
{
DataRow[] rows = dataTable.Select("FieldName='"+ userInput + "'");
}
and in validation you can do any check. right now you only want to check ' but in future, may be you will have to check some thing else.
and based on your need you can checge return type of validate function, if you want to modify input data then modify and return that else just return bool.
If you want to use DataTable.Select(filter) for data filter then you have to format/ignore or replace special character from filter statement and for that u will have to write more code. If you dont want to be panic for special character then you can use LINQ like
DataTable dataTable = new DataTable();
DataColumn dc = new DataColumn("FieldName");
dataTable.Columns.Add(dc);
DataRow dr = dataTable.NewRow();
dr[0] = "D'sulja";
dataTable.Rows.Add(dr);
string input = "D'sulja";
var result = from item in dataTable.AsEnumerable()
where item.Field<string>("FieldName") == input select item;
In this case, I think the single quote is the only character you have to worry about since it is used to delimit string values. For more information on expression syntax, see the MSDN entry for DataColumn.Expression (creating a filter expression uses the same rules as for the DataColumn.Expression property).
You don't indicate which version of C# you are using, but with LINQ, you can do this:
var rows = table.AsEnumerable()
.Where(r => r.Field<string>("Name") == "O'Hare")
.Select(r => r)
.ToArray();
One tradeoff is that you'll also need to check the RowState if you have any deleted rows in the DataTable, but it does provide another option.

Categories