I am using the code below to allow a user to filter a DataTable by searching a particular string that could be in any column or any row. The code needs to delete rows where the value doesn't exist as the DataTable is exported after the operation.
The problem with this code is two-fold: 1) it is extremely slow for larger tables, and 2) it can only find the complete contents of a cell (i.e. if the column "Name" has a row where the value is "Andrew" the user should be able to search "drew" or "and" and get that result; right now it will return that row if they search "Andrew").
if(!String.IsNullOrEmpty(combo1Text) || !String.IsNullOrEmpty(combo2Text)
&& !String.IsNullOrEmpty(search1Text) && !search1Text.Contains("Type your search for" + comboText + "here"))
{
for (int i = tab1table.Rows.Count - 1; i >= 0; i--)
{
DataRow dr = tab1table.Rows[i];
if (!dr.ItemArray.Contains(search1Text))
{
dr.Delete();
tab1table.AcceptChanges();
}
percentprogress++;
worker.ReportProgress(percentprogress);
}
}
What is the best way to do the filtering I want (and do so efficiently, so that it's not just looping through everything)?
To search if a cell content contains the searched text, try the following code:
for (int i = tab1table.Rows.Count - 1; i >= 0; i--)
{
DataRow dr = tab1table.Rows[i];
if (!dr.ItemArray.Any(x=>(x as string).Contains(search1Text)))
{
dr.Delete();
}
percentprogress++;
worker.ReportProgress(percentprogress);
}
tab1table.AcceptChanges();
If you have any column that isn't of string type, you should replace (x as string) to x.ToString()
Related
I'm trying to print the content of a DataTable, starting with the column headers, followed by the content of the table tupples.
output.Add($"Table : [{dataTable.TableName}]");
string strColumnNames = "";
foreach (DataColumn col in dataTable.Columns)
{
if (strColumnNames == "")
strColumnNames = col.ColumnName.PadLeft(col.MaxLength - col.ColumnName.Length); // (*)
else strColumnNames = strColumnNames + "|" +
col.ColumnName.PadLeft(col.MaxLength - col.ColumnName.Length); // (*)
}
output.Add($"[{strColumnNames}]");
foreach (DataRow dataRow in dataTable.Rows)
{
string temp = "";
for (int i = 0; i < dataRow.ItemArray.Count(); i++)
{
if (i == 0)
temp = dataRow.ItemArray[i].ToString(); // (**)
else temp += "|" + dataRow.ItemArray[i].ToString(); // (**)
}
output.Add($"[{temp}]");
}
The (*) parts in this code are using the MaxLength property of the DataColumns maximum length in order to get a column-like output.
I would like to do the same in the (**) parts, but I don't know how to access the corresponding DataColumn, starting from the dataRow object.
Does anybody have an idea?
Thanks in advance
You already have the dataTable instance available. dataTable.Columns[i] should give you the appropriate DataColumn.
Datatable has already been instantiated here. If you want to print the datacolumn you should use dataTable.Column[i] for the appropriate column.
I want to take the last non-null values of each column in a DataTable and create a single row with them.
Sample code:
DataTable temp; // temp is the DataTable shown on the left side in the "Current Result" section
DataTable temp2; // stores the newly create DataTable row
foreach(DataRow row in temp.Rows){
object[] Item = new object[] {};
foreach(DataColumn col in temp.Columns){
if (row[col] != null || row[col] != DBNull.Value || !String.IsNullOrWhiteSpace(row[col].ToString()))
{
Array.Resize(ref Item, Item.Length + 1);
Item[Item.Length - 1] = row[col];
}
}
temp2.Rows.Add(Item);
}
My code currently copies all of the cells from one DataTable to another, including the cells that don't have any values stored in it.
Current Result:
In the photo below, I blacked out all the cells except of the last non-values of each column. I want the shown values to be stored and displayed as a single row.
Desired Result:
I don't know if the problem is solved. Her are my suggestion to solve the problem. I would go from the last row up to the first, because the task is to have the last value from each column and then the loop is may be erlier finish, as when you go from the top to the bottom of the table. But this I think also depends on the data in the table.
DataTable temp = yourTable; // the original table
DataTable temp2 = new DataTable();
object[] lastNonNullValues = new object[temp.Columns.Count];
// Index of the last row.
int lastRow = temp.Rows.Count - 1;
// Get from the last row to the first
for (int i = lastRow; i >= 0; i--)
{
// Don't know if necessary but if all columns has an value -> finish.
if (lastNonNullValues.All(x => x != null)) break;
DataRow row = temp.Rows[i];
for (int j = 0; j < temp.Columns.Count; j++)
{
// Continue if some value was written
if (lastNonNullValues[j] != null) continue;
// None of this condition should be true, thas why should change from || to &&
if (row[j] != null && row[j] != DBNull.Value && !string.IsNullOrWhiteSpace(row[j].ToString()))
{
lastNonNullValues[j] = row[j];
}
}
}
temp2.Rows.Add(lastNonNullValues);
Notice:
The solution of Rufus L should also be work when change the if statement from or to and-conjunction.
It seems to me that you're just looking to add a single row to the temp2 table, which has the last non-null value for each column. If that's the case, then you can initialize the object[] with the Columns.Count size, and we can loop through the columns using the column index, which allows us to assign a new value to an existing column when we find a non-null value for it in the current row:
DataTable temp = new DataTable();
DataTable temp2 = temp.Clone();
// A single row of data that is 'Columns.Count' long
object[] lastNonNullValues = new object[temp.Columns.Count];
foreach (DataRow row in temp.Rows)
{
// Loop through columns using the column index rather than a foreach
for (int i = 0; i < temp.Columns.Count; i++)
{
var col = temp.Columns[i];
if (row[col] != null || row[col] != DBNull.Value ||
!string.IsNullOrWhiteSpace(row[col].ToString()))
{
// Now we just assign the value at the index for this column
lastNonNullValues[i] = row[col];
}
}
}
// Add our single row to the results table
temp2.Rows.Add(lastNonNullValues);
My question is actually more about optimizing something I already have working.
I'm having a hard time believing there isn't a better way to do this with a LINQ query or lambda expression, so I thought I'd try here.
Each of my datatable rows has an item number and 43 quantity columns, that each correspond with a specific day. What I'm trying to do is take each row, and find the first quantity column that is greater than 0 and return that column name. My solution does work, but I'd really like to make it more efficient:
foreach (DataRow r in dt.Rows)
{
for (int i = 3; i <= dt.Columns.Count - 1; i++)
{
tempCol = dt.Columns(i).ColumnName.ToString();
rowValue = Convert.ToInt32(r(tempCol));
if (rowValue > 0)
{
tempCol = tempCol.Replace("Apat", "");
break;
}
}
var FirstAvailableDate = WorkDate.AddDays((dec)tempCol).ToShortDateString;
//use data in someway
}
Thanks for any suggestions ahead of time!!
the current code, each row * each column
get name of column
store it in variable
in match case perform String.Replace
my suggestion:
var allCols = dt.Columns
.Cast<DataColumn>()
.Select(col => col.ColumnName.Replace("Apat", ""))
.ToArray();
foreach (DataRow r in dt.Rows)
{
var firstCol =
r.ItemArray.Select((cell, position) => Tuple.Create(Convert.ToInt32(cell), position))
.FirstOrDefault(tuple => tuple.Item1 > 0);
if(firstCol == null) continue;
var colName = allCols[firstCol.Item2];
var FirstAvailableDate = WorkDate.AddDays((dec)colName).ToShortDateString;
//use data in someway
}
Please change following code
Tuple.Create(Convert.ToInt32(position), cell)
var colName = allCols[firstCol.Item1];
Working fine...!!!
In my application i am filtering a datatable using a filter expression and am getting a DataRow which matches the condition.Now i want to check if the value of particular column exists in any row of DataRow array.
Code:
string FilterCond1 = "id=" + getId;
DataRow[] myrow = DataTable.Select(FilterCond1);
if (myrow.Length > 0)
{
//check for size=28 in DataRow[]
}
else
{
}
I have column size in the datatable DataTable and i want to check if any row of the DataRow array has a value 28 in the column size.How can i go about it?
Try this
string FilterCond1 = "id=" + getId;
DataRow[] myrow = DataTable.Select(FilterCond1);
if (myrow.Length > 0)
{
for(int i = 0; i < myrow.Length; i ++)
{
if(myrow[i]["size"].ToString() == "28")
{
// YOUR CODE HERE
}
}
}
else
{
}
EDIT
Just add the condition to your filter.
string FilterCond1 = "id=" + getId + " AND size=28";
Then you don't need the if(myrow[i]["size"].ToString() == "28") as you know the rows in the array are the one you want.
You can use column collection to access particular column value within row.
if(myrow[rowIndex]["ColoumnName"].ToString() == "somevalue")
Where row index could from zero to length-1
Edit based on comments, you can put multiple condition on column in select, check it here, and may not need to iterate.
string FilterCond1 = "id=" + getId + " AND size = " + 28;
DataRow[] myrow = dt.Select(FilterCond1);
To iterate through rows collection
for(int i=0; i < myrow.Length; i++)
{
if(myrow[i]["size"].ToString() == "28")
{
//your code
}
}
First you should aiterate through all rows using foreach then use the below code..
if(myrow[row]["size"] == 28)
or
int ColIndex = 3; // replace 3 with ur co. index
if(myrow[row][ColIndex] == 28)
How would i go about updating a single cell in a row so for example Row[0] i want to update the column "Value" with its current value +1. This is code i have which selects the single row but the Value updates with all the column "Value" data then +1
public void MetricChange()
{
DataTable dt = ds.Tables["MetricTable"];
int value = dt.AsEnumerable().Sum(r1 => r1.Field<int>("Value"));
if (Access.Checked)
{
ds.Tables["MetricTable"].Rows[0]["Value"] = value +1;
//ds.Tables["MetricTable"].Rows[1]["Value"] = value - 1;
Chart2.DataSource = ds.Tables["MetricTable"];
GridView1.DataSource = dt;
GridView1.DataBind();
}
}
How would i just update the "Value" column within a single row???
Currently you are modifying only a single row, you need to do that for each row in the data table.
for(int i = 0; i < ds.Tables["MetricTable"].Rows.Count;i++)
ds.Tables["MetricTable"].Rows[i]["Value"] = value +1;
Its always better to check the dataset against null and table existence. Something like.
if(ds != null && ds.Tables.Count > 0 && ds.Tables["MetricTable"] != null)
This is how you do it :P
int val = Convert.ToInt32(ds.Tables["MetricTable"].Rows[0]["Value"]) + 1;
Then Update the row by this ds.Tables["MetricTable"].Rows[0][1] = val