Linq to DataSet? - c#

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.

Related

Get column name by value of field in datarow

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...!!!

How to check duplicate rows in dataset?

I have a dataset which has duplicate rows i want my error message to execute when duplicate rows are present.
Below is my code please help
DataSet dsXml = new DataSet();
dsXml.ReadXml(new XmlTextReader(new StringReader(xml)));
Hashtable hTable = new Hashtable();
ArrayList duplicateList = new ArrayList();
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
if (hTable.Contains(drow))
{
duplicateList.Add(drow);
}
else
{
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
}
}
Your code is not working, because DataRow instances will be compared by references instead of comparing their fields. You can use custom comparer:
public class CustomDataRowComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
if (x.ItemArray.Length != y.ItemArray.Length)
return false;
for (int i = 0; i < x.ItemArray.Length; i++)
if (!x[i].Equals(y[i]))
return false;
return true;
}
public int GetHashCode(DataRow obj)
{
int hash = 17;
foreach (object field in obj.ItemArray)
hash = hash * 19 + field.GetHashCode();
return hash;
}
}
or use existing DataRowComparer which compares DataRow objects for equivalence by using value-based comparison:
HashSet<DataRow> set = new HashSet<DataRow>(DataRowComparer.Default);
// or: new HashSet<DataRow>(new CustomDataRowComparer());
foreach (DataRow row in dsXml.Tables[0].Rows)
{
if (!set.Add(row))
// duplicate row
}
You can also check if duplicated rows exist with Linq to DataSet query:
var duplicatedRowsExist = dsXml.Tables[0].AsEnumerable()
.GroupBy(r => r, DataRowComparer.Default)
.Any(g => g.Count() > 1);
You have to compare the content of the rows, not the rows themselves. Something like this should do it:
var hasDupes = dsXml.Tables[0].Rows
.AsEnumerable()
.GroupBy(row => new
{
row.Field<string>("Title"),
row.Field<string>("Address"),
row.Field<string>("State"),
row.Field<string>("City"),
row.Field<int>("Status"),
row.Field<int>("CreatedBy"),
row.Field<int>("UpdatedBy")
})
.Where(g => g.Count() > 1)
.Any();
if(hasDupes)
//Show error message
I think you have to alter you logic a little. You don't add the row to the hTable, so there are never duplicates. And I guess you have to show the message in the end, else the list will not be complete yet.
As stated by others, you do need Sergeys answer to get the comparison to work. If you have that covered, this code will solve the other logic problems.
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
if (!hTable.Contains(drow))
{
hTable.Contains(drow);
hTable.Add(drow);
}
else
{
duplicateList.Add(drow);
}
}
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
First, you need to define your comparison between rows. It appears when you create your hTable there is nothing in it, so the hTable.Contains call is always going to return false.
As a side note, you can't just compare a DataRow with another DataRow, it will use the default equality comparison (implemented using IEqualityComparer) and effectively boils down to a reference equality check, which none of the rows will be equal to each other.
Somewhere, you can either implement your own IEqualityCompariosn, or simply write a custom method to check the values of each row.
Here is the answer of my above Question
You can Check duplicate rows in dataset.. it is working fine try it.
DataSet dsXml = new DataSet();
dsXml.ReadXml(new XmlTextReader(new StringReader(xml)));
List<string> duplicateList = new List<string>();
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
string strr = "";
for (int j = 0; j < dsXml.Tables[0].Columns.Count; j++ )
{
strr += drow[j];
}
if (!duplicateList.Contains(strr))
{
duplicateList.Add(strr);
}
else
{
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
}
}

How to find rowindex of a datatable into another datatable?

I am using C#. I have two data tables and i want to find the rows of first data table into second data table.
Example.
First data table's data:
1 inam
2 sohan
Second data tables's data:
3 ranjan
1 inam
2 sohan
Now i want to know the index of first two rows of first data table into second data table.
Please help guys.
Any answer or advice
Best Regards
You can use following extension method which returns the first index of a "sub-sequence":
// I've used String.Join to get something that is comparable easily
// from the ItemArray that is the object-array of all fields
IEnumerable<string> first = table1.AsEnumerable()
.Select(r => string.Join(",",r.ItemArray)); //
IEnumerable<string> second = table2.AsEnumerable()
.Select(r => string.Join(",", r.ItemArray));
int index = second.IndexOfSequence(first, null); // 1
Here the extension:
public static int IndexOfSequence<TSource>(this IEnumerable<TSource> input, IEnumerable<TSource> sequence, IEqualityComparer<TSource> comparer)
{
if (input == null) throw new ArgumentNullException("input");
if (sequence == null) throw new ArgumentNullException("sequence");
if (!sequence.Any()) throw new ArgumentException("Sequence must not be empty", "sequence");
if (comparer == null)
{
comparer = EqualityComparer<TSource>.Default;
}
int index = -1;
int firstIndex = -1;
bool found = false;
TSource first = sequence.First();
using (IEnumerator<TSource> enumerator = input.GetEnumerator())
{
using (IEnumerator<TSource> enumerator2 = sequence.GetEnumerator())
{
enumerator2.MoveNext();
while (enumerator.MoveNext())
{
index++;
found = comparer.Equals(enumerator.Current, enumerator2.Current);
if (found && firstIndex == -1) firstIndex = index;
if (found && !enumerator2.MoveNext())
return firstIndex;
}
}
}
return -1;
}
tested with this sample data:
var table1 = new DataTable();
table1.Columns.Add("ID", typeof(int));
table1.Columns.Add("Name");
var table2 = table1.Clone();
table1.Rows.Add(1, "inam");
table1.Rows.Add(2, "Sohan");
table2.Rows.Add(3, "ranjan");
table2.Rows.Add(1, "inam");
table2.Rows.Add(2, "Sohan");
If you don't have much volume this might work....
var tableOneIndex = -1;
var tableTwoIndex = -1;
foreach (var tableOneRow in tableOne.Rows)
{
tableOneIndex++;
foreach (var tableTwoRow in tableTwo.Rows)
{
tableTwoIndex++;
if (tableOneRow["name"].ToString() == tableTwoRow["name"].ToString())
{
// Do whatever you wanted to do with the index values
}
}
}
As a simple solution, this should suffice:
// Create and populate data tables
DataTable dataTable1 = new DataTable();
dataTable1.Columns.Add("Name", typeof(string));
DataRow row1 = dataTable1.NewRow();
row1["Name"] = "Inam";
DataRow row2 = dataTable1.NewRow();
row2["Name"] = "Sohan";
dataTable1.Rows.Add(row1);
dataTable1.Rows.Add(row2);
DataTable dataTable2 = new DataTable();
dataTable2.Columns.Add("Name", typeof(string));
DataRow row3 = dataTable2.NewRow();
row3["Name"] = "Ranjan";
DataRow row4 = dataTable2.NewRow();
row4["Name"] = "Inam";
DataRow row5 = dataTable2.NewRow();
row5["Name"] = "Sohan";
dataTable2.Rows.Add(row3);
dataTable2.Rows.Add(row4);
dataTable2.Rows.Add(row5);
// Loop through rows in first table
foreach (DataRow row in dataTable1.Rows)
{
int rowIndexInSecondTable = 0;
// Loop through rows in second table
for (int i = 0; i < dataTable2.Rows.Count; i++)
{
// Check if the column values are the same
if (row["Name"] == dataTable2.Rows[i]["Name"])
{
// Set the current index and break to stop further processing
rowIndexInSecondTable = i;
break;
}
}
// The index of the row in the second table is now stored in the rowIndexInSecondTable variable, use it as needed, for example, writing to the console
Console.WriteLine("Row with name '" + row["Name"] + "' found at index " + rowIndexInSecondTable.ToString());
}

Find a string in all DataTable columns

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());
}

Creating datatables

I've a DataTable[having 5 columns] from db which has combined data.
I need to invoke a group by on this combined table and create 2 tables...one with groupedBy rows and other having items.
What would be fastest way to do this in C# code?
Also, I've written code below for adding columns for these 2 tables.Is that correct?
Heres my code:
string colName = "ACCOUNT_ID";
var allRows = combinedTable.AsEnumerable();
var accountRowGroups = allRows.GroupBy(row => row[colName]);
DataTable masterDataTable = new DataTable();
DataTable childPricesDataTable = new DataTable();
// Create the columns
DataColumnCollection pdCols = combinedTable.Columns;
for (int ndx = 0; ndx < pdCols.Count; ndx++)
{
string columnName = pdCols[ndx].ColumnName;
Type type = Type.GetType(pdCols[ndx].DataType.ToString());
masterDataTable.Columns.Add(columnName, type);
childPricesDataTable.Columns.Add(columnName, type);
}
See a similar question here: How to merge multiple DataTable objects with group by with C# and concatinating duplicate rows?
I agree with duffymo, do it in SQL rather than data in-memory.
However if that isn't an option:
You can add a relationship between the two DataTables: http://msdn.microsoft.com/en-us/library/ay82azad(v=vs.71).aspx
Then you can run group by's on the combined datatables: http://codecorner.galanter.net/2009/04/20/group-by-and-aggregates-in-net-datatable/
Here is a proper example from the SQL Team:
http://weblogs.sqlteam.com/davidm/archive/2004/05/20/1351.aspx
public static DataTable GROUPBY(DataTable Table, DataColumn[] Grouping, string[] AggregateExpressions, string[] ExpressionNames, Type[] Types)
{
if (Table.Rows.Count == 0)
return Table;
DataTable table = SQLOps.PROJECT(Table, Grouping);
table.TableName = "GROUPBY";
for (int i = 0; i < ExpressionNames.Length; i++)
{
table.Columns.Add(ExpressionNames[i], Types[i]);
}
foreach (DataRow row in table.Rows)
{
string filter = string.Empty;
for (int i = 0; i < Grouping.Length; i++)
{
//Determine Data Type
string columnname = Grouping[i].ColumnName;
object o = row[columnname];
if (o is string || DBNull.Value == o)
{
filter += columnname + "='" + o.ToString() + "' AND ";
}
else if (o is DateTime)
{
filter += columnname + "=#" + ((DateTime)o).ToLongDateString()
+ " " + ((DateTime)o).ToLongTimeString() + "# AND ";
}
else
filter += columnname + "=" + o.ToString() + " AND ";
}
filter = filter.Substring(0, filter.Length - 5);
for (int i = 0; i < AggregateExpressions.Length; i++)
{
object computed = Table.Compute(AggregateExpressions[i], filter);
row[ExpressionNames[i]] = computed;
}
}
return table;
}
It's not direct answer to your question but maybe alternative (I believe better) way to solve your problem.
If I understand correctly you have DataTable, some defined manipulations/filters and want return modfied DataTable as result. But in practice the result DataTable hasn't known shape (it may be different depending columns in filter).
Consider using linq instead of manipulating DataTables and DataRelations. Linq gives support for all mentioned by you operations: grouping, filtering, trimming and many more.
As the result you may return object which is bindable also, so you may use it in your grid, but the method definition will be much cleaner.

Categories