C# Datatable.select fails with # hash character in data - c#

I have a datatable containing a list of service calls placed each month. Column F4 contains the serial number of the machine, now all I wanted to do was find the number of service calls placed for each serial number and add the count to a new a column. I wrote the code below and all was working fine but then a problem arose when a hash sign '#' appeared in the data to reflect an unknown or yet assigned serial number. This caused the error "Cannot perform '=' operation on System.String and System.Int32." on the line "DataRow[] FoundRow = dt.Select("[F4] =" + chkStr);
The F4 column is a text/string and I even tried cloning the datatable and manually assigning the column to .DataType = typeof(String); I know its the # sign giving the issue as if I remove from the data for testing purposing all is fine. Can anyone shed some light on this. Many thanks.
// datatable dt contains all my monthly call data
dt.Columns.Add("Counter", typeof(System.Int32)); // add a count column to the datatable
for (int i = 0; i < dt.Rows.Count; i++) //loop through the datatable
{
string chkStr = dt.Rows[i]["F4"].ToString(); // get 1st serial num, then each other one in loop
DataRow[] FoundRows = dt.Select("[F4] =" + chkStr); // OK now select all the table rows containing this serial, place in FoundRows
Int32 count = FoundRows.Length; // get the length which is a count of the same serial num / service calls
dt.Rows[i]["Counter"] = count;
}

Don't worry about counting the value as in Int32. Who cares; you can still "use" the number when it is a string. Read it as a string, "#" will be valid and numbers will be valid. Here is a simple solution that will even tell you how many have not been assigned yet.
public class GroupCounter {
private Dictionary<string, int> serialNumbers;
public GroupCounter() {
this.serialNumbers = new Dictionary<string, int>();
}
public Dictionary<string, int> Count(DataTable table) {
foreach (DataRow row in table.Rows) {
int counter;
string serial = row.Field<string>("COLUMN");
if(!this.serialNumbers.TryGetValue(serial, out counter)){
this.serialNumbers.Add(serial, 1);
}
counter++;
}
return this.serialNumbers;
}
}
Since this returns a Dictionary you can use dictionary[key] (your serial number) to fetch to count for any serial number.

many thanks for your suggestions but found the answer was simply to add single quote marks ' around the chkStr as below and the code then handled the # hash characters fine. Appreciate your time.
DataRow[] FoundRows = dt.Select("[F4] ='" + chkStr + "'");

Related

Condition of if/else in C# not behaving as expected

I'm experiencing weird behavior in an if/else statement written in C#. I am new to C# and a novice with ASP.NET using Visual Studio Pro 2017.
I am working on adding functionality to a web app to allow for uploading of SQL data to table via user uploading a .CSV per this article by Mudassar Khan
All works well with this. I am now working to validate the data before the upload takes place.I am using the same .CSV from the article above but with a few "error" conditions introduced.
1,John,Hammond,United States
2,Shouldbreak,India
3,JohnMathews,Ireland
4,Robert,Schidner,Russia,sdfsdfdf,sdfsdfsdf,
In the code below you will see that I am testing to be sure there are the correct number of columns in each row by setting the required number of columns in a variable and comparing that to the number of commas found in each row present in the .CSV.
My desired validation is as follows:
If there are a correct number of columns add to dataTable.
If there are not the correct amount of columns.
Display ASP control which contains the error message.
If there are too many columns.
Add a string to the error message that include the row number and the incorrect number of columns.
If there are too few columns.
Add a string to the error message variable that includes the row number and the incorrect number of columns.
I am also tracking and displaying the number of commas found in each row in a separate ASP control for debugging purposes.
Below is the code in my code-behind file:
//Upload and save the file
string csvPath = Server.MapPath("~/_uploadedDataCSV/") + Path.GetFileName(FileUpload1.PostedFile.FileName);
FileUpload1.SaveAs(csvPath);
DataTable dt = new DataTable();
int columnCount = 4; // Expected number of columns
int commas = 0; // Set comma count to 0;
int tableRow = 0; // set current table row to 0;
Count.Text = string.Empty;
string errMessage = string.Empty;
dt.Columns.AddRange(new DataColumn[4] // Add expected number of columns to table
{
// Add column Header and expected (c#)datatype
new DataColumn("Id", typeof(int)),
new DataColumn("First Name", typeof(string)),
new DataColumn("Last Name", typeof(string)),
new DataColumn("Country",typeof(string))
});
string csvData = File.ReadAllText(csvPath); // Creates String of all content in CSV file
foreach (string row in csvData.Split('\n')) // Do the following for each row in the file
{
// Start Validation Logic
// - - - - - - - - - - - - - - - - - - - -
tableRow++;
// Count commas in row
foreach (char ch in row)
{
if (ch.Equals(','))
{
commas++;
}
}
if (!string.IsNullOrEmpty(row)) // if the row is not empty
{
if (commas == columnCount) // if commas in row equals the number of columns expected
{
dt.Rows.Add();
int i = 0;
foreach (string cell in row.Split(','))
{
dt.Rows[dt.Rows.Count - 1][i] = cell;
i++;
}
}
//Begin error reporting for row
// - - - - - - - - - - - - - - - - - - - -
else if (commas != columnCount)
{
err.Visible = true; // show error message element
err.Text = errMessage; // set text of error message element to value of errMessage variable
if (commas >= columnCount) // if too many columns in row
{
errMessage+= $"Row {tableRow} has too many columns <br />";
}
else if (commas < columnCount) // if not enough columns in row
{
errMessage+= $"Row {tableRow} does not have enough columns <br />";
}
}
}
Count.Text+= commas.ToString() + "|";
commas = 0;
}
The issue I am having is when there are too many columns in a row, I am not getting the error message associated with this condition. The debug controller properly displays the number of commas and if there are too few, displays the message properly, just not the message for too many. When I step through the code in debugger the variables (commas / columnCount) are reported corrected for all the rows.
Any suggestions? I am happy to provide more information if I left anything out.
I have tried removing the condition of too few columns and it still doesn't display the message for too many columns. I have also tried reversing the order of the check but the same output is given. I have tried rebuilding per this discussion.
All credits should go to #Mike McCaughan, his comment is absolutely in place. I just want to give a little example as of what was stated by Mike:
string a = string.Empty;
string b = a;
a += "3";
Console.WriteLine(b); //result in empty string in console
back to your problem:
string errMessage = string.Empty;
err.Text = errMessage;
errMessage+= $"Row {tableRow} has too many columns <br />";
Console.WriteLine(err.Text); //result in empty string in console
The reason why it happens is because of the immutable nature of string in c#. Means any changes to the string object will result in a new object creation. You can find more information there: Immutability of String Objects

How to loop through all cells of a column and retrieve values

I can't seem to access the actual values in my column; My values for each row end up all being the same and actual row count. I can't seem to figure this out. Any help would be appreciated. Here's my current method:
private string converter() {
string binaryString = string.Empty;
int cellValue = 0;
int colIndex = 3;
foreach(DataGridViewRow row in this.dataGridView1.Rows)
{
cellValue = (int) row.Cells[colIndex].Value;
binaryString = parseString(cellValue);
}
return binaryString;
}
You are replacing the value of "binaryString" in each loop duration.
I'm not sure what your goal is, but perhaps this what you meant:
binaryString += parseString(cellValue);
Notice the +=
Edit - Another Approach?
Perhaps you want each value in a column to be converted? Try this, then.
private List<String> converter()
{
List<String> binaryStrings = new List<String>();
int cellValue = 0;
int colIndex = 3;
foreach(DataGridViewRow row in this.dataGridView1.Rows)
{
cellValue = (int) row.Cells[colIndex].Value;
binaryStrings.Add(parseString(cellValue));
}
return binaryStrings;
}
This gives you a list of converted strings. One string per row.
If you are using a DataTable I do not think you can make an Expression to convert a base 10 number to a binary string representation. However it is possible to add the binary string to the cell whenever a DataRow changes. If a DataRow changes in the DataTable, then the code can do the conversion of the base 10 value to a binary string representation and add the string to the proper cell.
In order to achieve this, the code needs to register the RowChanged event BEFORE the DataTable is filled. In addition, since a value is changed in the RowChanged event, it is necessary to turn off this event when the code adds the binary string to the table. This will prevent an obvious infinite loop, which would keep calling itself every time the binary string is added to the row.
You will need to change the names to match your requirements. Below is the line needed to register the RowChanged event with the DataTable. Note you need to set this line BEFORE you fill the table with data.
gridTable.RowChanged += new DataRowChangeEventHandler(Row_Changed);
Then all we need is this Row_Changed method. NOTE, the event is unregistered from the DataTable to avoid an infinite loop.
private static void Row_Changed(object sender, DataRowChangeEventArgs e) {
gridTable.RowChanged -= new DataRowChangeEventHandler(Row_Changed);
DataRow dr = e.Row;
int base10 = (int)dr["Base10"];
string binary = Convert.ToString(base10, 2);
dr["Binary"] = binary;
gridTable.RowChanged += new DataRowChangeEventHandler(Row_Changed);
}
I am guessing there may be other ways to accomplish this, the goal here was to avoid having to loop through the grid to set the binary strings. Hope this helps.
i was looking for a solution to your problem...
here i what i came with..
works here.
foreach (DataGridViewColumn column in dataGridView1.Columns)
{
if(column.Name == "wanted column name")
{
for(int iRow = 0; iRow < dataGridView1.RowCount; iRow++)
{
string data = (string)dataGridView1[column.Index, iRow].Value;
//Do something with data
}
}
}

c# Read lines from a file and replace with text from DataGridView Data

I am relatively new to c#, I am creating an windows application which would read all the lines from a text file. The user will input the string which needs to be replaced in Column[0] and the text with which it needs to be replaced in Column1 of the DataGridView control.
I have created two string arrays column0 and column1.
However, I am getting an error while replacing the string in line (column0, column1)
The following is my code:
string[] column0 = new string[dgvMapping.Rows.Count];
string[] column1 = new string[dgvMapping.Rows.Count];
int j = 0;
foreach(DataGridViewRow row in dgvMapping.Rows)
{
if (!string.IsNullOrEmpty(Convert.ToString(row.Cells[0].Value)))
{
column0[j] = Convert.ToString(row.Cells[0].Value);
column1[j] = Convert.ToString(row.Cells[1].Value);
j++;
}
}
var _data = string.Empty;
String[] arrayofLine = File.ReadAllLines(ofd.FileName);
using (StreamWriter sw = new StreamWriter(ofd.FileName + ".output"))
{
for (int i = 0; i < arrayofLine.Length; i++)
{
string line = arrayofLine[i];
line = line.Replace(column0[i], column1[i]);
sw.WriteLine(line);
}
}
I am using OpenFileDialog to select the file.
The Error While Executing:
You are looping around a file of unknown number of lines, and assuming that the count of lines in the grid is exactly the same as that of the file. Your code will only work if both the file and the gridView have the same number of lines.
One of the solutions, is to loop over the array of lines (as you have already did), and search for the GridViewRow in which the current line contains a key in your DGV. If this is the case, then replace all the occurences of the key by the value (obtained from the gridView) in that line, otherwise do nothing.
Check out the code below :
// Convert the row collection to a list, so that we could query it easily with Linq
List<DataGridViewRow> mySearchList = dataGridView1.Rows.Cast<DataGridViewRow>().ToList();
const int KEY_INDEX = 0; // Search index in the grid
const int VALUE_INDEX = 1; // Value (replace) index in the grid
for (int i = 0; i < arrayofLines.Length; i++)
{
string line = arrayofLines[i];
// Get data grid view Row where this line contains the key string
DataGridViewRow matchedRow = mySearchList.FirstOrDefault(obj => line.Contains(obj.Cells[KEY_INDEX].Value.ToString()));
// If this row exists, replace the key with the value (obtained from the grid)
if (matchedRow != null)
{
string key = matchedRow.Cells[KEY_INDEX].Value.ToString();
string value = matchedRow.Cells[VALUE_INDEX].Value.ToString();
line = line.Replace(key, value);
sw.WriteLine(line);
}
else
{
// Otherwise, do nothing
}
}
Stuartd is correct… there are more lines in the file than there are elements to search. I am not sure what the search is doing in a sense that it seems somewhat limited. The code appears to search for each item depending on what line it is. The searched value in column 0 and the replace value in column 1 of row 0… will only replace those values for the FIRST line in the file. The DataGridViews second row values will search/replace only the SECOND line and so on. This seems odd.
Example the two string arrays (column0 and column1) have sizes set to the number of rows in dgvMapping. Let’s say there are 5 rows in the grid, then the array sizes will be 5 strings. When you start the loop to write the strings, the loop starts at 0 and stops at the number of lines in the file. The code uses this i variable as an index into the two arrays. If there are more lines in the file, than there are rows in the grid… then you will get the error.
Again, this seems odd to do the search and replace this way. Assuming you want to search for EACH term in all the rows in column 0 and replace the found searched string with the replace string in column 1, then you will need to loop through EACH row of the grid for EACH line in the file. This will replace ALL the search/replace terms in the grid with ALL the lines in the file. If this is what you what to accomplish below is one way to achieve this, however…there are possibly better ways to accomplish this.
The code below reads the file into one big string. Then the code loops through ALL the grid rows to search/replace the strings in the big string. Hope this helps.
string bigString = File.ReadAllText(ofd.FileName);
try {
using (StreamWriter sw = new StreamWriter(ofd.FileName + ".output")) {
for (int k = 0; k < dgvMapping.Rows.Count; k++) {
if (dgvMapping.Rows[k].Cells[0].Value != null && dgvMapping.Rows[k].Cells[1].Value != null) {
string searchTerm = dgvMapping.Rows[k].Cells[0].Value.ToString();
string replaceTerm = dgvMapping.Rows[k].Cells[1].Value.ToString();
if (searchTerm != "") {
bigString = bigString.Replace(searchTerm, replaceTerm);
} else {
// one of the terms is empty
}
} else {
// one of the terms is null}
}
}
sw.WriteLine(bigString);
}
}
catch (Exception ex) {
MessageBox.Show("Write Erro: " + ex.Message);
}

jQuery calculate column count from excel range

I have a C# web form where a user inputs data about an excel file into a number of text boxes, including:
TXT_FirstDataRow - integer field which represents the first row that contains data
TXT_LastDataColumn - string field which represents the last column that contains data
TXT_Range - string field which contains the calculated data range using the above two fields
TXT_ColumnCount - The number of columns in the calculated range
I am currently using jQuery to automatically calculate the data range using the code below which works perfectly:
$('#TXT_FirstDataRow,#TXT_LastDataColumn').keyup(function () {
var row = $('#TXT_FirstDataRow').val();
var column = $('#TXT_LastDataColumn').val();
var range = 'A' + row + ':' + column; //All ranges start at column A
$('#TXT_Range').val(range);
});
I would now like to automatically populate the TXT_ColumnCount with the count of the columns. For example:
Range |Column Count
------+------------
A1:C |3
A7:BX |76
I imagine that this should be made easier as the ranges will always be starting from column A, so the column count should just be the equivalent column number of TXT_LastDataColumn, but I am afraid even this is beyond my slightly limited jQuery knowledge.
Any help would be greatly appreciated.
Thanks
I found an answer on SO that helped me, link below:
Convert excel column alphabet (e.g. AA) to number (e.g., 25)
function letterToNumbers(string) {
string = string.toUpperCase();
var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', sum = 0, i;
for (i = 0; i < string.length; i++) {
sum += Math.pow(letters.length, i) * (letters.indexOf(string.substr(((i + 1) * -1), 1)) + 1);
}
return sum;
}

Dropdown removing the leading zeros in C#

I have a database in MS Access with one table field named "Seat number" with the text data type having entries 001, 002........100, 62AB. While populating the values, there were no issues but when I try to select seat numbers having leading zeros (e.g. 001,002,009,010 etc.) by doing
string seatList =(ddlSeatList.SelectedValue.ToString());
it removes the leading zero(es).
Assuming that you have up to three digits in seat you can use
string seatList =(ddlSeatList.SelectedValue.ToString("000"));
UPDATE:
Solutions above is completely wrong.
Based on comments dropdown list is initialized in the following way
if (seatselection=="9F")
{
seatalloaction = ("select TCS_Seat_ID,TCS_Seat_Num from TCS_Seat where TCS_Seat_Enable_F=" + "'Y'");
}
DataSet dataTCSSeat = new DataSet();
dataTCSSeat = drpDownList.ExecuteQuery(seatalloaction);
ddlSeatList.Items.Add(new ListItem("TCS_Seat_ID", "TCS_Seat_Num"));
if (dataTCSSeat.Tables.Count > 0)
{
for (int i = 0; i < dataTCSSeat.Tables[0].Rows.Count; i++)
{
ddlSeatList.Items.Add(new ListItem(
dataTCSSeat.Tables[0].Rows[i].ItemArray[1].ToString(),
dataTCSSeat.Tables[0].Rows[i].ItemArray[0].ToString()));
}
}
Thus ddlSeatList.SelectedValue returns values from _TCS_Seat_ID_ field, not from _TCS_Seat_Num_ as visualized. If it is required to get exacly the value from dropdown as it is shown in list control must be initialized in the following way:
for (int i = 0; i < dataTCSSeat.Tables[0].Rows.Count; i++)
{
//both text and value are initialized with TCS_Seat_Num field values
ddlSeatList.Items.Add(new ListItem(
dataTCSSeat.Tables[0].Rows[i].ItemArray[1].ToString(),
dataTCSSeat.Tables[0].Rows[i].ItemArray[1].ToString()));
}

Categories