How to retrieve Distinct string values from comparing two Arrays? - c#

I want to retrieve Absent student names from sql database.
I have a Listbox in which i have Present student Rfid data (Something like 42 39 A0 11), I have stored the student details along with Rfid in database as nvarchar(1500) datatype.
Using the present stud id in list box i want to retrieve absent students name in an List.
Then i thought of using foreach loop to remove the students who's id was in the Listbox
But when i defined the two list like total and present with values and tried to remove the string from total which are in present the nthe output was successful
private void checkabst_Click(object sender, EventArgs e)
{
string[] present1 = new string[listbox_present.Items.Count];
List<string> present = new List<string> { ""};
List<string> absent = new List<string>();
List<string> total = new List<string>();
using (SqlConnection con = new SqlConnection(cs))
{
SqlCommand cmd = new SqlCommand("Select Rfid_Uid From Studetails", con);
con.Open();
SqlDataReader sdr = cmd.ExecuteReader();
while (sdr.Read())
{
total.Add(sdr[0].ToString());
}
}
present = listbox_present.Items.Cast<string>().ToList();
foreach(string temp in present)
{
total.Remove(temp);
}
foreach (string temp in total)
{
listbox_absent.Items.Add(temp);
}
}
Stuck here from past few days.
The problem i think the listbox values are giving trouble while removing the string from total list.

From your comments in Ehsan's answer above I am not sure you know about:
System.Linq.Enumerable.Distinct

You can achieve this simply by an Except method. No need to for loops
Remove following code
bool isfound = false;
for (int i = 0; i < 3; i++)
{
isfound = false;
for (int j = 0; j < present.Length; j++)
{
if (allstd[i] == present[j])
{
isfound = true;
}
}
if (!isfound)
{
MessageBox.Show(allstd[i]);
}
}
and add
absent = allstd.Except(present).ToArray();
and iterate over these to show in MessageBox.
foreach (var stud in absent)
{
MessageBox.Show(stud);
}
Just to clarify after OP comment , Give a working example
string[] present = { "A" , "C" , "D" ,"P"};
string[] absent = new string[3];
string[] allstd = { "A", "B", "C" ,"D" , "E" , "P"};
absent= allstd.Except(present).ToArray();
absent will contain "B" and "E"
Update after OP's latest code update.
Try replacing your method with following method with removed tow foreach loops
private void checkabst_Click(object sender, EventArgs e)
{
string[] present1 = new string[listbox_present.Items.Count];
List<string> present = new List<string> { ""};
List<string> absent = new List<string>();
List<string> total = new List<string>();
using (SqlConnection con = new SqlConnection(cs))
{
SqlCommand cmd = new SqlCommand("Select Rfid_Uid From Studetails", con);
con.Open();
SqlDataReader sdr = cmd.ExecuteReader();
while (sdr.Read())
{
total.Add(sdr[0].ToString());
}
}
present = listbox_present.Items.Cast<string>().ToList();
absent = total.Except(present).ToArray();
foreach (var stud in absent)
{
MessageBox.Show(stud);
}
}

Related

How do I read SQL row values of a column into an array?

I have created a method that will get data from a SQL table and store the columns of data each in their own array. Right now, when working through the debugger, what I notice is that when I am assigning values to these arrays, they are null values.
I did check to see if my query returns values in SSMS and it indeed does. So null values should not be expected.
Here is the code to the method:
public static CommentsPageData getComments(string wcNum)
{
string[] prodTimeArray = new string[24];
string[] crewsArray = new string[24];
string[] commentsArray = new string[24];
string[] redTimeArray = new string[24];
string[] greenTimeArray = new string[24];
string commandSql = "SELECT TOP 24 Production_Time, Crew, Red_Time, Green_Time, Comment FROM ************ WHERE Work_Center = #wc ORDER BY Production_Time DESC";
SqlConnection con = new SqlConnection("Data Source=*******;Initial Catalog=********;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand(commandSql, con);
cmd.Parameters.AddWithValue("wc", wcNum);
CommentsPageData commPageData = new CommentsPageData();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
prodTimeArray.Append(reader["Production_Time"].ToString());
crewsArray.Append(reader["Crew"].ToString());
redTimeArray.Append(reader["Red_Time"].ToString());
greenTimeArray.Append(reader["Green_Time"].ToString());
commentsArray.Append(reader["Comment"].ToString());
}
}
else
{
Console.WriteLine("No rows found");
}
reader.Close();
}
commPageData.ProdTimes = prodTimeArray;
commPageData.Crews = crewsArray;
commPageData.GreenTime = greenTimeArray;
commPageData.RedTime = redTimeArray;
commPageData.Comments = commentsArray;
con.Close();
return commPageData;
}
Long story short, I have created a Class (CommentsPageData) which has an array for each column I'm returning 24 values from. However... The problem is in the while(reader.Read()){} section of the method. I can see it assigning values, but it is just assigning null values.
How can I actually get the values and assign them to my array correctly?
Its just like Jeroen Mostert said, Arrays in C# do not change in size after they are declared. You have declared your arrays inside the class which means the size you have initialized your arrays with is 0 which means no matter how many times you try to append elements to them, the arrays will be null. Use System.Collections.Generic List<Type> to hold your data as it can be updated to hold more elements.
public static CommentsPageData getComments(string wcNum)
{
List<string> prodTimeArray = new List<string>();
List<string> crewsArray = new List<string>();
List<string> commentsArray = new List<string>();
List<string> redTimeArray = new List<string>();
List<string> greenTimeArray = new List<string>();
string commandSql = "SELECT TOP 24 Production_Time, Crew, Red_Time, Green_Time, Comment FROM ************ WHERE Work_Center = #wc ORDER BY Production_Time DESC";
SqlConnection con = new SqlConnection("Data Source=*******;Initial Catalog=********;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand(commandSql, con);
cmd.Parameters.AddWithValue("wc", wcNum);
CommentsPageData commPageData = new CommentsPageData();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
prodTimeArray.Add(reader["Production_Time"].ToString());
crewsArray.Add(reader["Crew"].ToString());
redTimeArray.Add(reader["Red_Time"].ToString());
greenTimeArray.Add(reader["Green_Time"].ToString());
commentsArray.Add(reader["Comment"].ToString());
}
}
else
{
Console.WriteLine("No rows found");
}
reader.Close();
}
commPageData.ProdTimes = prodTimeArray.ToArray();
commPageData.Crews = crewsArray.ToArray();
commPageData.GreenTime = greenTimeArray.ToArray();
commPageData.RedTime = redTimeArray.ToArray();
commPageData.Comments = commentsArray.ToArray();
con.Close();
return commPageData;
}
I would use this code
reader = cmd.ExecuteReader()
var dt=new DataTable;
dt.Load(reader);
Dictionary<string, string[]> dict = new();
for (var i = 0; i < dt.Rows.Count; i++)
{
for (var j = 0; j < dt.Rows[i].ItemArray.Length; j++)
{
if (!dict.ContainsKey(dt.Columns[j].ColumnName))
dict.Add(dt.Columns[j].ColumnName, new string[dt.Rows.Count]);
dict[dt.Columns[j].ColumnName][i] = dt.Rows[i].ItemArray[j].ToString();
}
}
and just use the dictionary
but if you want a name for each array
string[] prodTimeArray;
string[] crewsArray;
string[] commentsArray;
....
prodTimeArray = dict["Production_Time"];
crewsArray = dict["Crew"];
commentsArray = dict["Comment"];
....
If you really need to have this end result, I'd recommend just making an object representing a row in the table, use Dapper to return the rows into an IEnumerable of the objects, then to a LINQ select for each column into an array
For Example:
var results = await con.QueryAsync<MyRowObject>(commandSql, new {"wc"}, commandType: Text);
var productionTimeArray = results.Select(x => x.ProductionTime).ToArray();

Returning Data Rows to List<string> with SqlDataReader

I'm trying to create a generic SqlDataReader which converts a table with 33 columns into a list. I would like each list item to contain all 33 column values for each row.
However, my code is assigning each value to an individual list item.
So instead of 1000 list items = 1000 rows of data, I have 33,000 list items.
I would prefer to use a list over a datatable, because the list comparisons I need to do are much simpler.
How can I have 1000 list items with 33 values each?
public static List<string> loadSQL(String query, String connectString)
{
List<string> dataList = new List<string>();
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i ++)
{
dataList.Add(Convert.ToString(reader.GetValue(i)));
}
}
}
}
return dataList;
}
}
... update ...
corrected to the following. It returns the list items correctly. However, my list contains 33,000 items containing 33 items each. How can I control the loop so it stops after 1000 rows?
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
List<string> tempRow = new List<string>();
for (int i = 0; i < reader.FieldCount; i ++)
{
tempRow.Add(Convert.ToString(reader.GetValue(i)));
}
dataList.Add(tempRow);
}
}
}
}
The best option for you to do this task is DataTable, But you don't want to use it. So, the net option will be, Create a class based on the query-output then use a List<objectOftheClass>. But in your case, the Input query will be changed all times so a common class will not be meaningful Since you are trying to make it generic. So the option you can follow is List<List<string>> or List<List<object>>. As per this the method signature will be like the following:
public static List<object[]> loadSQL(string query, string connectString)
{
List<object[]> dataList = new List<object[]>();
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
object[] tempRow = new object[reader.FieldCount];
for (int i = 0; i < reader.FieldCount; i++)
{
tempRow[i] = reader[i];
}
dataList.Add(tempRow);
}
}
}
}
return dataList;
}
Why List<object>? why not `List?:
The reader will give you the column data as the same type of column in the table. If it is object then you need not convert it every time.
** Note:-** Change String to string for the arguments in the method signature. You can find a reason here
You can use a List<List<string>> like this:
public static List<List<string>> loadSQL(String query, String connectString)
{
List<List<string>> dataList = new List<List<string>>();
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
var l = new List<string>();
for (int i = 0; i < reader.FieldCount; i ++)
{
l.Add(Convert.ToString(reader.GetValue(i)));
}
dataList.Add(l);
}
}
}
return dataList;
}
}
You can use an array within the list to achieve what you are trying to do. Here is a quick example using your code:
public static List<string[]> loadSQL(String query, String connectString)
{
List<string[]> dataList = new List<string[]>();
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
int rowcounter = 0;
while (reader.Read())
{
string[] value = new string[reader.FieldCount];
for (int i = 0; i < reader.FieldCount; i ++)
{
value[i] = Convert.ToString(reader.GetValue(i));
}
dataList.Add(value);
rowcounter++;
}
}
}
return dataList;
}
Alertnately, if you want to use the List, you will need to embed the values a single string, using a comma separator or something similar.

My dataset doesn't work properly

My problem is that when it gets updated it adds the previous data which was in it Again and again
and i use a telerik grid view
here my code in 3 layers
first one
private void btnSbmt_Click(object sender, EventArgs e)
{
foreach (var row in radGridView1.Rows)
{
_MyName.Add((string)row.Cells[1].Value);
}
foreach (var row in radGridView1.Rows)
{ // 0 - first column
_MyAmount.Add((int)row.Cells[2].Value);
}
foreach (var row in radGridView1.Rows)
{
_MyPrice.Add((decimal)row.Cells[3].Value);
}
Ref_View_Model = new View_model._View_Model();
Ref_View_Model.GetInsertProduct(_myName, _myAmount, _myPrice, txtDt.Text);
radGridView1.CurrentRow.Delete();
productTableAdapter.Update(sales_and_Inventory_SystemDataSet);
productTableAdapter.Fill(sales_and_Inventory_SystemDataSet.Product);
MessageBox.Show("Product(s) were added", "Done", MessageBoxButtons.OK);}
second one
public void GetInsertProduct( List<string> _name, List<int> _amount, List<decimal> _price, string _date)
{
Ref_Model = new Model._Model();
Ref_Model.InsertProduct( _name, _amount, _price, _date);
}
and the Third one
public void InsertProduct(List<string> _myName,
List<int> _myAmount,
List<decimal> _myPrice, string _date)
{
Connection_String = myconnection string
Query = #"INSERT INTO dbo.product(Name, Amount, Price, [date])
VALUES(#Name, #Amount, #Price, #Date);";
using ( Con = new SqlConnection(Connection_String))
using ( Cmd = new SqlCommand(Query, Con))
{
Cmd.Parameters.Add("#Name", SqlDbType.NVarChar);
Cmd.Parameters.Add("#Amount", SqlDbType.Int);
Cmd.Parameters.Add("#Price", SqlDbType.Decimal);
// Cmd.Parameters.Add("#Date", SqlDbType.NVarChar);
Cmd.Parameters.Add("#Date", SqlDbType.DateTime).Value = Convert.ToDateTime(_date);
Cmd.Connection = Con;
Con.Open();
int recordsToAdd = _myName.Count();
for(int x = 0; x < recordsToAdd; x++)
{
Cmd.Parameters["#Name"].Value = _myName[x];
Cmd.Parameters["#Amount"].Value = _myAmount[x];
Cmd.Parameters["#Price"].Value = _myPrice[x];
Cmd.Parameters["#Date"].Value = _date;
Cmd.ExecuteNonQuery();
}
}
}
It seems that you are using global variables to keep the values that you read from the grid. If you don't clear them after the first insert, you have still the values in the global lists and you add them again to the datatable
Of course you can use just one loop to reload the global variables with the actual values present in the grid
private void btnSbmt_Click(object sender, EventArgs e)
{
// This removes whatever is in the lists
_MyName.Clear();
_MyAmount.Clear();
_MyPrice.Clear();
// and now start adding items from scratch
foreach (var row in radGridView1.Rows)
{
_MyName.Add((string)row.Cells[1].Value);
_MyAmount.Add((int)row.Cells[2].Value);
_MyPrice.Add((decimal)row.Cells[3].Value);
}
....

Why it always shows the error index is out of array bounds?

I'm crafting an app in c#, here is a problem that stopped me. My database table has 7 columns and it takes 7 while running application. But in the line SingleRow[c] = data[0].ToString();
it shows me an error telling me the index is out of bounds of array. Please suggest. My code is below.
public string[] RowOnly()
{
string[] SingleRow = new string[] { };
conn = new OleDbConnection(connStr);
conn.Open();
cmd = new OleDbCommand(query, conn);
data = cmd.ExecuteReader();
MessageBox.Show(data.HasRows.ToString());
int i = data.FieldCount;
while (data.Read())
{
for (int c = 0; c < i; c++)
{
SingleRow[c] = data[0].ToString();
}
}
conn.Close();
return SingleRow;
}
string[] SingleRow = new string[] { };
Is creating an array of length 0
Do this:
string[] SingleRow = new string[i];
(You'll have to move it to after the line that initializes i)
i.e:
int i = data.FieldCount;
string[] SingleRow = new string[i];
Tip: Only use {} when you want to initalize the collection:
e.g. new string[]{"abc", "def"};
This solved the problem thanks #Maximilian Ast
public string[] RowOnly()
{
conn = new OleDbConnection(connStr);
conn.Open();
cmd = new OleDbCommand(query, conn);
data = cmd.ExecuteReader();
MessageBox.Show(data.HasRows.ToString());
int i = data.FieldCount;
List<string> SingleRow = new List<string>();
while (data.Read())
{
for (int c = 0; c < i; c++)
{
SingleRow.Add(data[c].ToString());
}
}
conn.Close();
return SingleRow.ToArray();
}

Create a function two compare 2 arrays - one with data from csv, one with data from database

I am trying to create a function that compares the results of 2 arrays. If they match, then nothing needs to be done. If they do not match, then an UPDATE to a SQL table (called Products_Extension) needs to take place.
Array A is meant to contain the comma-separate list of ItemsAssignedIDs (this field is called uidProducts in the Products_Extension table) for each Product Profile from a .csv file uploaded by the user. Array B is meant to contain a comma-separate list of ItemsAssignedIDs from the database for each Product Profile. Both lists are attached to a Product Profile through the ProfileID field. (I am guessing that I need to clear the items from Array B is A and B don't match, populate Array B with items from Array A, then update the database accordingly)
I have two functions, one that builds a list of ItemsAssignedIDs for each Product Profile in the database, and another that gets all ItemsAssignedIDs from uploaded .csv files. But I am confused on how to combine them so that I can compare the two Arrays.
Function that builds comma-separated list of ItemsAssignedIDs for each Product Profile:
private string BuildItemsAssignedList(int ProfileID)
{
string assignedIdList = "";
string sqlQuery = "SELECT [uidProducts] FROM Products_Extension WHERE uidProductProfile = #ProfileID";
using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["AbleCommerce"].ToString()))
{
SqlCommand cmd = new SqlCommand(sqlQuery, cn);
cmd.Parameters.Add(new SqlParameter("#ProfileID", ProfileID));
cmd.CommandType = CommandType.Text;
cn.Open();
using (IDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
if (!String.IsNullOrEmpty(assignedIdList))
assignedIdList += ", ";
assignedIdList += (reader["uidProducts"].ToString());
}
reader.Close();
}
cn.Close();
}
return assignedIdList = string.Join(",", assignedIdList);
}
Function that splits the comma-separated list of ItemsAssignedIDs from a .csv file for each Product Profile:
private void SaveAssignedItems(int ProfileID, string[] row)
{
string AssignedID = GetValue(row, (int)ProfileColumns.ItemsAssignedIDs);
AssignedID = AssignedID.ToLower();
AssignedID.Trim();
if (AssignedID != "")
{
string[] assignedids = AssignedID.Split(',');
foreach (string item in assignedids)
{
int MyVal = Convert.ToInt32(item);
using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["AbleCommerce"].ToString()))
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "UPDATE Products_Extension SET uidProductProfile = #ProfileID WHERE uidProducts=#AssignedID";
cmd.Parameters.AddWithValue("#ProfileID", ProfileID);
cmd.Parameters.AddWithValue("#AssignedID", item);
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
}
}
}
}
As far as i understood you want to compare two arrays and perform some operation if matched/don't match right?
Please try the following:
Method 1
bool found= false;
int[] a = new int[] { 8, 1, 2, 3, 4 };
int[] b = new int[] { 5, 6, 7, 8 };
foreach (var numberA in a)
{
foreach (var numberB in b)
{
if (numberA == numberB)
{
found= true;
}
}
if(!found)
{
//numberA not found in array B
}
}
Mehtod 2:
int[] a = new int[] { 1, 2, 3, 4 };
int[] b = new int[] { 1, 2, 3 };
var result = a.Except(b).ToArray();
foreach (var item in result)
{
Response.Write("\nItems not in B are :"+item);
}
NOTE: Please do not use 'as is'. make changes accordingly..!

Categories

Resources