Hey everyone I have the following code
Console.WriteLine("y: {0}", liveData.Tables["Players"].Rows.Count); //Shows full table
DataRow[] foundRows;
foundRows = liveData.Tables["Players"].Select("FName like 'Marc'");
foreach (DataRow dr in foundRows)
{
Console.WriteLine("Index is " + dr.Table.Rows.IndexOf(dr));
}
Now when i return my index of the row at variable dr that shows correctly
What i want to do is where it states Fname and Marc i want to pass those as variables and not manually input the string, is this possible?
Example, sometimes i may need to search Lname and whatever that is or a different first name?
Thank you for looking a bit stuck but again probably a simple answer :)
If I am not mistaken, you wish to have the query select a name from FName that resembles an input from an user (in this case 'Marc').
If thats the case, your best bet is to have something ask for the input beforehand and assign that value into a variable from a textbox for example.
String Queryname = TextBox1.Text
Console.WriteLine("y: {0}", liveData.Tables["Players"].Rows.Count); //Shows full table
DataRow[] foundRows;
foundRows = liveData.Tables["Players"].Select("FName like " + Queryname);
foreach (DataRow dr in foundRows)
{
Console.WriteLine("Index is " + dr.Table.Rows.IndexOf(dr));
}
Forgive me if I did not get your question. Let me know if that works and if not, try clarifying with an example.
string SelectQuery=//create query based on user input
foundRows = liveData.Tables["Players"].Select(SelectQuery);
For example if user wants to search based on Last Name, then Lname+ like + search term.You need to map the search attribute with column names.
Can't you just build the string with variables?: .Select(param+" like '"+value+"'")
You could create a function
public DataRow[] MySelect(string column, string name)
{
return liveData.Tables["Players"].Select(string.Format("{0} LIKE '{1}'", column, name));
}
And then use it like this
foundRows = MySelect("LName", "John");
Related
I'm trying to get record of a row using DataRow. Here's what I've done so far:
uID = int.Parse(Request.QueryString["id"]);
PhotoDataSetTableAdapters.MembersTableAdapter mem = new PhotoDataSetTableAdapters.MembersTableAdapter();
PhotoDataSet.MembersDataTable memTable = mem.GetMemberByID(uID);
DataRow[] dr = memTable.Select("userID = uID");
string uName = dr["username"].ToString();
Then I got the error:
Cannot implicitly convert type 'string' to 'int'
The error points to "username". I don't know what's wrong because I'm just trying to assign a string variable to a string value. Anyone figures out the reason of the error? Please help and thanks.
Change the following statement
DataRow[] dr = memTable.Select("userID = uID");
To
DataRow[] dr = memTable.Select("userID = "+ uID);
dr is a DataRow[] not a DataRow, therefor the compiler complains that you pass a String when he needs an int for the index.
You actually want the username of the the single DataRow in the DataTable, am i right?
String uName = memTable.AsEnumerable().Single().Field<String>("username");
Note that this throws an exception if there is more than one row in the DataTable. But since you pass an ID to the DataAdapter, i assume that it should return only one record.
Seems there are two problems. One is as Asif suggest that uID should be "userId = " + uID (should probably cast uID as string) and that dr is an array of datarows as Tim points out. You can access it by index too: dr[0]["userName"].ToString()
I have a DataTable dt with 2 columns. First col (call it CustomerId) is unique and doesn't allow nulls. the second one allows nulls and is not unique.
From a method I get a CustomerId and then I would like to either insert a new record if this CustomerId doesn't exist or increment by 1 what's in the second column corresponding to that CustomerId if it exists.
I'm not sure how I should approach this. I wrote a select statement (which returns System.Data.DataRow) but I don't know how to test whether it returned an empty string.
Currently I have:
//I want to insert a new row
if (dt.Select("CustomerId ='" + customerId + "'") == null) //Always true :|
{
DataRow dr = dt.NewRow();
dr["CustomerId"] = customerId;
}
If the datatable is being populated by a database. I would recommend making the customerid a identity column. That way when you add a new row it will automatically create a new customerid which will be unique and 1 greater than the previous id (depending on how you setup your identity column)
I would check the row count which is returned from the select statement. Something like
I would also use string.Format...
So it would look like this
var selectStatement = string.Format("CustomerId = {0}", customerId);
var rows = dt.Select(selectStatement);
if (rows.Count < 1){
var dr = dt.NewRow();
dr["CustomerId"] = customerId;
}
This is my method to solve similar problem. You can modify it to fit your needs.
public static bool ImportRowIfNotExists(DataTable dataTable, DataRow dataRow, string keyColumnName)
{
string selectStatement = string.Format("{0} = '{1}'", keyColumnName, dataRow[keyColumnName]);
DataRow[] rows = dataTable.Select(selectStatement);
if (rows.Length == 0)
{
dataTable.ImportRow(dataRow);
return true;
}
else
{
return false;
}
}
The Select Method returns an array of DataRow objects. Just check if its length is zero (it's never null).
By the way, don't write such statements in the code directly as in this example. There's a technique for breaching your code's security called "SQL Injection", I encourage you to read the Wikipedia Article. In brief, an experienced user could write SQL script that gets executed by your database and potentially do harmful things if you're taking customerId from the user as a string. I'm not experienced in database programming, this is just "general knowledge"...
I am retrieving data from an MSSQL server using the SqlDataAdapter and DataSet. From that DataSet I am creating a DataTable. My goal is to convert each column of the table into a string where the elements are comma delimited. I figured that I would try the string conversion first before making the delimiter work.
The code runs in the code-behind of an ASP.Net page. The ultimate goal is to pass the string to a jscript variable, it's a "functional requirement" that I create a delimited string from the columns and that it has to end up as a jscript variable.
Here's what I have thus far:
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet);
DataTable temperature = myDataSet.Tables["Table"];
// LOOP1
foreach (DataRow row in temperature.Rows)
// this loop works fine and outputs all elements
// of the table to the web page, this is just to
// test things out
{
foreach (DataColumn col in temperature.Columns)
{
Response.Write(row[col] + " ### ");
}
Response.Write("<br>");
}
// LOOP2
foreach (DataColumn column in temperature.Columns)
// this loop was meant to take all elements for each
// column and create a string, then output that string
{
Response.Write(column.ToString() + "<br>");
}
In LOOP1 things work fine. My data has 4 columns, all are appropriately rendered with one record per row on the web page.
I saw the code for LOOP2 at http://msdn.microsoft.com/en-us/library/system.data.datacolumn.tostring.aspx which seems to do exactly what I need except it does not actually do what I want.
The only thing LOOP2 does is write 4 lines to the web page. Each line has the header of the respective table column but none of the additional data. Clearly there's either a logic flaw on my part or I misunderstand how DataColumn and .toString for it works. Please help me out on this one. Thanks in advance.
EDIT:
Here's an SQL query result example, this is what the Table looks like:
Table quesry result # ImageShack
What I want to end up are four strings, here's an example for the string that would be created from the second column: "-6.7, -7, -7.2, -7.3, -7.3".
This code will concatenate values from cells under each column with ", ":
foreach (var column in temperature.Columns)
{
DataColumn dc = column as DataColumn;
string s = string.Join(", ", temperature.Rows.OfType<DataRow>()
.Select(r => r[dc]));
// do whatever you need with s now
}
For example, for DataTable defined as:
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("Column #1"));
table.Columns.Add(new DataColumn("Column #2"));
table.Rows.Add(1, 2);
table.Rows.Add(11, 22);
table.Rows.Add(111, 222);
... it will produce "1, 11, 111" and "2, 22, 222" strings.
Edit: I saw you chose to declare column as var as opposed to DataColumn, is that a matter of personal preference/style or is there an issue with coding?
Consider following scenario (on the same data table example as above):
// we decide we'll use results later, storing them temporarily here
List<IEnumerable<string>> columnsValues = new List<IEnumerable<string>>();
foreach (DataColumn column in temperature.Columns)
{
var values = temperature.Rows.OfType<DataRow>()
.Select(r => r[column].ToString())
columnsValues.Add(values);
}
We assume we now got list of list of column values. So, when we print them, like this:
foreach (var lisOfValues in columnsValues)
{
foreach (var value in listOfValues)
{
Debug.Write(value + " ");
}
Debug.WriteLine("");
}
We expect to see 1 11 111 followed by 2 22 222. Right?
Wrong.
This code will output 2 22 222 twice. Why? Our .Select(r => r[column].ToString()) captures column variable - not its value, but variable itself - and since we don't use it immediately, once we're out of loop all we know is last value of column.
To learn more about this concept search for closures and captured variables - for example, in posts like this.
Summary:
In this very case you can go with DataColumn in foreach statement. It doesn't matter here because we're enumerating through our .Select(r => r[dc]) either way inside the loop (precisely, string.Join does that for us), producing results before we get to next iteration - whatever we capture, is used immediately.
The link you have posted clearly states
The Expression value, if the property
is set; otherwise, the ColumnName
property.
and that is what is happening. You get column names.
This could help: How to convert a DataTable to a string in C#?
I have created a solution which read a large csv file currently 20-30 mb in size, I have tried to delete the duplicate rows based on certain column values that the user chooses at run time using the usual technique of finding duplicate rows but its so slow that it seems the program is not working at all.
What other technique can be applied to remove duplicate records from a csv file
Here's the code, definitely I am doing something wrong
DataTable dtCSV = ReadCsv(file, columns);
//columns is a list of string List column
DataTable dt=RemoveDuplicateRecords(dtCSV, columns);
private DataTable RemoveDuplicateRecords(DataTable dtCSV, List<string> columns)
{
DataView dv = dtCSV.DefaultView;
string RowFilter=string.Empty;
if(dt==null)
dt = dv.ToTable().Clone();
DataRow row = dtCSV.Rows[0];
foreach (DataRow row in dtCSV.Rows)
{
try
{
RowFilter = string.Empty;
foreach (string column in columns)
{
string col = column;
RowFilter += "[" + col + "]" + "='" + row[col].ToString().Replace("'","''") + "' and ";
}
RowFilter = RowFilter.Substring(0, RowFilter.Length - 4);
dv.RowFilter = RowFilter;
DataRow dr = dt.NewRow();
bool result = RowExists(dt, RowFilter);
if (!result)
{
dr.ItemArray = dv.ToTable().Rows[0].ItemArray;
dt.Rows.Add(dr);
}
}
catch (Exception ex)
{
}
}
return dt;
}
One way to do this would be to go through the table, building a HashSet<string> that contains the combined column values you're interested in. If you try to add a string that's already there, then you have a duplicate row. Something like:
HashSet<string> ScannedRecords = new HashSet<string>();
foreach (var row in dtCSV.Rows)
{
// Build a string that contains the combined column values
StringBuilder sb = new StringBuilder();
foreach (string col in columns)
{
sb.AppendFormat("[{0}={1}]", col, row[col].ToString());
}
// Try to add the string to the HashSet.
// If Add returns false, then there is a prior record with the same values
if (!ScannedRecords.Add(sb.ToString())
{
// This record is a duplicate.
}
}
That should be very fast.
If you've implemented your sorting routine as a couple of nested for or foreach loops, you could optimise it by sorting the data by the columns you wish to de-duplicate against, and simply compare each row to the last row you looked at.
Posting some code is a sure-fire way to get better answers though, without an idea of how you've implemented it anything you get will just be conjecture.
Have you tried Wrapping the rows in a class and using Linq?
Linq will give you options to get distinct values etc.
You're currently creating a string-defined filter condition for each and every row and then running that against the entire table - that is going to be slow.
Much better to take a Linq2Objects approach where you read each row in turn into an instance of a class and then use the Linq Distinct operator to select only unique objects (non-uniques will be thrown away).
The code would look something like:
from row in inputCSV.rows
select row.Distinct()
If you don't know the fields you're CSV file is going to have then you may have to modify this slightly - possibly using an object which reads the CSV cells into a List or Dictionary for each row.
For reading objects from file using Linq, this article by someone-or-other might help - http://www.developerfusion.com/article/84468/linq-to-log-files/
Based on the new code you've included in your question, I'll provide this second answer - I still prefer the first answer, but if you have to use DataTable and DataRows, then this second answer might help:
class DataRowEqualityComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
// perform cell-by-cell comparison here
return result;
}
public int GetHashCode(DataRow obj)
{
return base.GetHashCode();
}
}
// ...
var comparer = new DataRowEqualityComparer();
var filteredRows = from row in dtCSV.Rows
select row.Distinct(comparer);
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.