dt = ds.Tables[1];
foreach (EPSFromElement element in elementList)
{
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
var ColumnName = column.ColumnName;
var ColumnData = row[column].ToString();
var currentElement = Regex.Replace(element.Field_Label, #"\W", "");
if (element.Module_Field_ID != null)
{
if (currentElement == ColumnName)
element.ColumnValue = ColumnData;
}
else
{
if (element.Field_Type_Name != "Checkbox")
{
if ("Q_" + element.Column_Name_ID == ColumnName)
element.ColumnValue = ColumnData;
}
else
{
if ("Q_" + element.Column_Name_ID + "_" + element.Value_Column_Name_ID == ColumnName)
element.ColumnValue = ColumnData;
}
}
}
}
}
I have a List of object(EPSElement). In EPSElement there is a field ColumnValue that gets data from a data table (dt = ds.Tables[1];). To achieve this i am using there foreach loop .Is there a better way to do this. I am not too happy with three for each loop. Can this be replaced by LINQ.
I would never attempt to replace that with LINQ. First, you're mutating inside the body of the foreach and that's a bad idea to attempt to do with LINQ. LINQ is about querying (the 'Q' in LIN Q is for q uery) and so shouldn't have side effects. But, even supposing that you were to replace the mutation with a projection to a new sequence of objects, converting that to LINQ would be very hard to read; too much nested logic. Leave it alone.
This translates reasonably well. Each of the foreach-es translate into their own from, the few variables are each lets, and all of the ifs end up being where's.
var query = from element in elementList
from row in dt.Rows.Cast<DataRow>()
from column in dt.Columns.Cast<DataColumn>()
let ColumnName = column.ColumnName
let ColumnData = row[column].ToString()
let currentElement = Regex.Replace(element.Field_Label, #"\W", "")
where (element.Module_Field_ID != null && currentElement == ColumnName)
|| (element.Field_Type_Name != "Checkbox"
&& "Q_" + element.Column_Name_ID == ColumnName
|| element.Field_Type_Name == "Checkbox"
&& "Q_" + element.Column_Name_ID + "_" + element.Value_Column_Name_ID == ColumnName)
select new { Element = element, ColumnData = ColumnData };
foreach (var item in query)
item.Element.ColumnValue = item.ColumnData;
Note that since the logic in the Where is a bit complex, it may be best to refactor it out into it's own method in which you have something like:
where IsValid(element, ColumnName, currentElement)
The implementation can then be the logic in either my query or your original code, based on which you'd prefer.
Related
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...!!!
I have taken a csv, made from an excel file, and put it into a data table. There are cells from the excel csv file that are empty and when I iterate through them, they are also iterated through. I wish to not iterate over them.
foreach (DataRow datarow in sorted.Rows)
{
Boolean first = true;
Console.Write(Environment.NewLine);
foreach (var item in datarow.ItemArray)
{
if (item != null)
{
int i = 0;
if (first)
first = false;
else
Console.Write(",");
Console.Write(item);
}
else
break;
}
}
I have tried the above and it still iterates through the empty cells.
Expanding on JohnD answer, you can try this, assuming you only want to output the fields to the Console:
var text = string.Empty;
foreach (DataRow datarow in sorted.Rows)
{
var items = datarow.ItemArray
.Where(x => ((x != null) && !string.IsNullOrEmpty(x.ToString())));
var textJoined = string.Join(",", items);
text += textJoined + Environment.NewLine;
}
Console.WriteLine(text);
You may not be familiar with LINQ, so you will need the following using statement:
using System.Linq;
Again this solution assumes you only want to output the values to the console window, it does not assume you want to iterate through all the columns for a given row. If that is what you want let me know and I can make the appropriate modifications
[edit]
whoops just re-read your question and it appears you do want to iterate through each column, so here is a solution below:
var text = string.Empty;
foreach (DataRow datarow in sorted.Rows)
{
var items = datarow.ItemArray
.Where(x => ((x != null) && !string.IsNullOrEmpty(x.ToString())));
var currentLine = string.Empty;
foreach(var item in items)
{
// do something with item
// in this case append to currentLine
currentLine += item + ",";
}
text += currentLine.Substring(0, currentLine.Length - 2) + Environment.NewLine;
}
Console.WriteLine(text);
You get the same result, you can now just do what you need for each item
Assuming that the item is really a string, check to see if item is null or empty using String.IsNullOrEmpty().
if (item != null && !String.IsNullOrEmpty(item.ToString()))
Additionally, replace the break statement with a continue
Why not like this?
// ItemArray is object[]
foreach (var item in datarow.ItemArray)
{
if (first)
{
first = false;
}
else
{
// I think you are expecting to see the comma here even the previous element is empty e.g. A,B,,D (that missing C)
Console.Write(",");
}
// so here we cannot guarantee "if (item is string)"
if (item != null)
{
Console.Write(item.ToString());
}
}
(My habit to wrap all codes in {})
foreach (DataRow datarow in sorted.Rows)
{
Boolean first = true;
Console.Write(Environment.NewLine);
foreach (var item in datarow.ItemArray)
{
if (!string.IsNullOrEmpty((item ?? "").ToString()))
{
int i = 0;
if (first)
first = false;
else
Console.Write(",");
Console.Write(item);
}
else
continue;
}
}
So i'm importing an excel doc into my project
DataSet result = excelReader.AsDataSet();
if (result != null)
{
foreach (System.Data.DataTable t in result.Tables)
{
if (t != null && t is System.Data.DataTable)
{
System.Data.DataTable table = t as System.Data.DataTable;
Items Lastitem = new Items();
foreach (System.Data.DataRow r in t.Rows)
{
if (r != null && r is System.Data.DataRow)
{
//new ItemType
if (r.ItemArray[0] != null)
{
Then I run checks to check which column is empty when I get the empty column i then want to get the empty row.
I have tried:
if (checkIfColumnisEmpty(r.ItemArray[0]) && !checkIfColumnisEmpty(r.ItemArray[1]))
{
throw new ImportBOQException("Error importing document: First column is empty at row " + r);
r being the datarow, the telesense allows me rowError, rowState etc... but not row Number....any ideas please? }
You could use a For Loop instead of Foreach when iterating through your rows as follows :
for (int i = 0; i < t.Rows.Count; i++)
{
// Do Your Null Checking and throw the exception with iteration number + 1
if (checkIfColumnisEmpty(t.Rows[i].ItemArray[0]) && !checkIfColumnisEmpty(t.Rows[i].ItemArray[1]))
{
throw new ImportBOQException("Error importing document: First column is empty at row " + (i + 1));
}
}
I am trying to find a fast way to find a string in all datatable columns!
Followed is not working as I want to search within all columns value.
string str = "%whatever%";
foreach (DataRow row in dataTable.Rows)
foreach (DataColumn col in row.ItemArray)
if (row[col].ToString() == str) return true;
You can use LINQ. It wouldn't be any faster, because you still need to look at each cell in case the value is not there, but it will fit in a single line:
return dataTable
.Rows
.Cast<DataRow>()
.Any(r => r.ItemArray.Any(c => c.ToString().Contains("whatever")));
For searching for random text and returning an array of rows with at least one cell that has a case-insensitive match, use this:
var text = "whatever";
return dataTable
.Rows
.Cast<DataRow>()
.Where(r => r.ItemArray.Any(
c => c.ToString().IndexOf(text, StringComparison.OrdinalIgnoreCase) > 0
)).ToArray();
If you want to check every row of every column in your Datatable, try this (it works for me!).
DataTable YourTable = new DataTable();
// Fill your DataTable here with whatever you've got.
foreach (DataRow row in YourTable.Rows)
{
foreach (object item in row.ItemArray)
{
//Do what ya gotta do with that information here!
}
}
Don't forget to typecast object item to whatever you need (string, int etc).
I've stepped through with the debugger and it works a charm. I hope this helps, and good luck!
This can be achieved by filtering. Create a (re-usable) filtering string based on all the columns:
bool UseContains = false;
int colCount = MyDataTable.Columns.Count;
string likeStatement = (UseContains) ? " Like '%{0}%'" : " Like '{0}%'";
for (int i = 0; i < colCount; i++)
{
string colName = MyDataTable.Columns[i].ColumnName;
query.Append(string.Concat("Convert(", colName, ", 'System.String')", likeStatement));
if (i != colCount - 1)
query.Append(" OR ");
}
filterString = query.ToString();
Now you can get the rows where one of the columns matches your searchstring:
string currFilter = string.Format(filterString, searchText);
DataRow[] tmpRows = MyDataTable.Select(currFilter, somethingToOrderBy);
You can create a routine of search with an array of strings with the names of the columns, as well:
string[] elems = {"GUID", "CODE", "NAME", "DESCRIPTION"};//Names of the columns
foreach(string column in elems)
{
string expression = string.Format("{0} like '%{1}%'",column,
txtSearch.Text.Trim());//Search Expression
DataRow[] row = data.Select(expression);
if(row.Length > 0) {
// Some code here
} else {
// Other code here
}
}
You can get names of columns by using ColmunName Method. Then, you can search every column in DataTable by using them. For example, follwing code will work.
string str = "whatever";
foreach (DataRow row in dataTable.Rows)
{
foreach (DataColumn column in dataTable.Columns)
{
if (row[column.ColumnName.ToString()].ToString().Contains(str))
{
return true;
}
}
}
You can create a filter expression on the datatable as well. See this MSDN article. Use like in your filter expression.
string filterExp = "Status = 'Active'";
string sortExp = "City";
DataRow[] drarray;
drarray = dataSet1.Customers.Select(filterExp, sortExp, DataViewRowState.CurrentRows);
for (int i=0; i < drarray.Length; i++)
{
listBox1.Items.Add(drarray[i]["City"].ToString());
}
I created a datatable with two column and four rows. I am trying to retrieve information from the row by linq based on the information I provide in the query statement for one column, but I get nothing in console.write statement.
var super =
from lang in JapanesePhrases.AsEnumerable()
where lang.Field<string>("Meaning") == "Song of Truth"
select lang.Field<string>("Phrase");
foreach (string item in super)
{
Console.Write(item + "\n");
}
i tried now this code:
var table = new DataTable();
table.Columns.Add("Meaning");
table.Columns.Add("Phrase");
for (int i = 0; i < 5; i++)
{
var row = table.NewRow();
row["Meaning"] = "Meaning"+i;
row["Phrase"] = "Phrase"+i;
table.Rows.Add(row);
}
var super = from lang in table.AsEnumerable()
where lang.Field<string>("Meaning") == "Meaning1"
select lang.Field<string>("Phrase");
foreach (string item in super)
{
Console.Write(item + "\n");
}
Console.ReadLine();
running seamlessly.
i should check data in datatable.