How to improve search speed in data grid view - c#

I have a datagridview which contain student details (first name, last name, gender, degree and burn date)
I write this code to perform "keyboard search" (for this example I "load" the data locally):
public class StudentDetails
{
public string firstName;
public string lastName;
public string gender;
public string degree;
public DateTime burnDate;
};
public List<StudentDetails> studentSearchList = new List<StudentDetails>(); // Contain all dgv for searches
private void Form1_Load(object sender, EventArgs e)
{
refreshDataGridView();
}
private void refreshDataGridView()
{
for (int i = 0; i < 1000; i++)
{
StudentDetails sd = new StudentDetails();
sd.firstName = GetRandomFirstName();
sd.lastName = GetRandomLastName();
sd.gender = GetRandomGender();
sd.degree = GetRandomDegree();
sd.burnDate = GetRandomBurnDate();
studentSearchList.Add(sd);
addRowToDataGrid(sd);
}
}
bool detailsContain(StudentDetails sd, string s)
{
if (sd.firstName.ToLower().Contains(s) ||
sd.lastName.ToLower().Contains(s) ||
sd.gender.ToLower().Contains(s) ||
sd.degree.ToLower().Contains(s) ||
sd.burnDate.ToShortDateString().ToString().Contains(s))
return true;
else
return false;
}
void addRowToDataGrid(StudentDetails sd)
{
string[] row = new string[] { sd.firstName, sd.lastName, sd.gender, sd.degree, sd.burnDate.ToShortDateString().ToString() };
dataGridView1.Rows.Add(row);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
List<StudentDetails> list_SD = new List<StudentDetails>();
String s = textBox1.Text;
s = s.ToLower().Trim();
foreach (StudentDetails sd in studentSearchList)
if (detailsContain(sd, s)) list_SD.Add(sd);
// Clear old rows
dataGridView1.Rows.Clear();
dataGridView1.Refresh();
// Adding the new rows
foreach (StudentDetails sd in list_SD)
addRowToDataGrid(sd);
}
My problem is the searching is too slowly and I have no idea to improve the speed.
I tried to declare another list that contain all database and to fill studentSearchList just with the last search result until the user press on delete character but it's not so help and it's cost memory…
Maybe someone have good search algorithm to suggest ?
Thank you

You need to keep your records "StudentDetails" in the array, not in the list.
Thereafter, build additional index-array (unsigned int or short)
for each searchable field, and sort indices in those arrays according values
of appropriate fields.
Thereafter, you will just use binary search for each necessary field, which is log(N).

You should first measure what takes time. Is it the search itself (I doubt) or refreshing the datagrid on every keystroke ?

Related

How to I assign my Combo Box's SelectedIndex to Array coordinates?

I have an array:
decimal[,] SmoothieListDecimal = { {5.99M, 6.99M, 7.99M, 8.99M},
{6.99M, 7.99M, 8.99M, 9.99M} }
And I have two combo boxes:
cmbSize and cmbStyle
cmbSize has: Small, Medium, Large, and King, which make up the values in the array.
The second combo box is cmbStyle, which includes only two selections, "Regular" and "Organic". The "Organic" prices are $1.00 more expensive and derived from the second row.
So, for example, if a user selects a "Medium" size and "Regular" style, the price would be pulled from row 1, column 1 in the array.
My question is, how do I set my variables to their respective array coordinates, and furthermore, how would I code the equation to handle this price selection?
I am using (Visual Studio 2015 and C#)
Thanks!
How about creating a class containing informations about smoothie prices:
public class SmoothiePrices
{
public string Description { get; set; }
public Dictionary<string, double> SizeAndPrice { get; set; }
}
Fill this with your info:
private List<SmoothiePrices> prices = new List<SmoothiePrices>();
public void InitSmoothies
{
prices.Add(new SmoothiePrices()
{
Name = "Regular",
SizeAndPrice = new Dictionary<string, double>()
{
{"Small", 5.99},
{"Normal", 6.99}
// And so on
};
});
}
Now you have created a data structure and can fill the combos:
private void InitStyleCombo()
{
this.cmbStyle.DisplayMember = "Description";
this.cmbStyle.DataSource = this.prices;
}
The last thing to do is to fill the cmbSize depending on the selection of the cmbStyle combo.
private void cmbStyle_SelectedIndexChanged(object sender, EventArgs e)
{
var smoothiePrice = this.cmbStyle.SelectedValue as SmoothiePrice;
this.cmbSize.DisplayMember = "Key";
this.cmbSize.ValueMember = "Value";
this.cmbSize.DataSource = smoothiePrice.SizeAndPrice;
}
To access the selected price for the size use:
var selectedPrice = (double)this.cmbSize.SelectedValue;
There are a lot of ways to do this, but one easy way (but certainly not the best or most robust) is to load the ComboBox.Items in the correct order for SmoothieListDecimal.
decimal price;
int column = cmbSize.SelectedIndex; // 0=Small,1=Medium,2=Large,3=King
int row = cmbStyle.SelectedIndex; // 0=Regular,1=Organic
if (column < 0)
MessageBox("Please select a size");
else if (row < 0)
MessageBox("Please select a style");
else
price = SmoothieListDecimal[row, col];

C# insert text after each occurence of a text string using wildcards [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
Solved:
This solution has been solved. I was modding a game and am creating a C# windows form to easily manipulate large quantities of repeating data, or expressions, and quickly insert mass changes to a file, or files. I was stuck on identifying a regular expression with Regex in my data files and inserting a text box field of data right after the expression I identified. The expression I have works in the regex editor, thanks to #Kris, but when I deployed it into my program, nothing happened. I apologize for not having my thoughts in order, I will be sure to be more clear next time I use SO.
Below is the data I wanted to manipulate, followed by the working code I was able to fix myself with pointers from #Kris and #Rufus L. Again, I needed to search for particular data strings and insert a new string underneath the data property in every occurrence in the file. Hope this helps someone.
The data properties text looks like this:
1234 = {
workers = {
culture = dudes
religion = awesome
size = 37800
}
professionals = {
culture = dudes
religion = awesome
size = 6000
}
leaders = {
culture = dudes
religion = awesome
size = 500
}
}
1235 = {
workers = {
culture = dudes
religion = awesome
size = 37800
}
professionals = {
culture = dudes
religion = awesome
size = 6000
}
leaders = {
culture = dudes
religion = awesome
size = 500
}
}
I only want to insert text into the parent #### = {} property that holds the child = {} property fields. IE, I want to insert textBox2.Text in a new line under 1234 = {
I have the regex expression linked on here, thanks to #Kris.
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
static string[] files;
static int curNum = 0;
static string[] txtFiles;
static string[] provinces;
public Form1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
System.IO.File.WriteAllText(pathText.Text + txtFiles[curNum], richTextBox1.Text);
}
private void prevButton_Click(object sender, EventArgs e) {
if(curNum > 0)
curNum--;
richTextBox1.Text = System.IO.File.ReadAllText(pathText.Text + txtFiles[curNum]);
filenameLabel.Text = txtFiles[curNum];
}
private void loadButton_Click(object sender, EventArgs e) {
curNum = 0;
txtFiles = GetFileNames(pathText.Text, "*.txt");
richTextBox1.Text = System.IO.File.ReadAllText(pathText.Text + txtFiles[curNum]);
filenameLabel.Text = txtFiles[curNum];
}
static string[] GetFileNames(string path, string filter) {
files = Directory.GetFiles(path, filter);
for (int i = 0; i < files.Length; i++)
files[i] = Path.GetFileName(files[i]);
return files;
}
private void nextButton_Click(object sender, EventArgs e) {
if(curNum < txtFiles.Length)
curNum++;
richTextBox1.Text = System.IO.File.ReadAllText(pathText.Text + txtFiles[curNum]);
filenameLabel.Text = txtFiles[curNum];
}
private void appendButton_Click(object sender, EventArgs e)
{
provinces = Regex.Matches(richTextBox1.Text, #"\d+(.*?){")
.Cast<Match>()
.Select(m => m.Value)
.ToArray();
for (int i = 0; i < provinces.Length; i++)
{
richTextBox1.Text = richTextBox1.Text.Replace(provinces[i], provinces[i] + System.Environment.NewLine + " " + textBox2.Text);
}
}
}
}
Use regex
\d+(.*?){
tested here
Another way to do it would be to insert the text at the end of each block, by searching for two } characters separated by 0 or more whitespace characters. The regex for this would look like:
Regex.Matches(searchString, "}\\s}");
In either case, when inserting the strings we should start from the last match (at the end of the string) and move backwards towards the beginning, because each insert will change the string length and therefore impact the index at which we want to do the insertion.
For example:
/// <summary>
/// Returns a string with newText inserted into parentText
/// </summary>
/// <param name="newText">The new text to insert</param>
/// <param name="parentText">The parent text to insert into</param>
/// <returns>The parent string with the newText inserted</returns>
private string GetInsertedText(string newText, string parentText)
{
var newParentText = parentText;
var matches = Regex.Matches(newParentText, "}\\s}");
// Replace from the last occurrence, since each insert will
// change the length of our string and alter the insert location
for(int index = matches.Count - 1; index >= 0; index--)
{
var match = matches[index];
newParentText = newParentText.Substring(0, match.Index + 1) +
Environment.NewLine + newText +
newParentText.Substring(match.Index + 1);
}
return newParentText;
}
You also might want to create a method that adds 4 spaces to the beginning of each line of the textBox1.Text so that it has a proper indent after it's inserted. For example:
private string GetIndentedText(string originalText, string indentText = " ")
{
return $"{indentText}{originalText}".Replace("\n", $"\n{indentText}").TrimEnd();
}
The usage of this would look something like:
private void button1_Click(object sender, EventArgs e)
{
// Check for any text requirements here (like it should contain
// an open brace, an equals sign, end with a close brace, etc.)
if (textBox1.Text.Length > 0)
{
// Insert the new (indented) text into our RichTextBox
richTextBox1.Text = GetInsertedText(GetIndentedText(textBox1.Text),
richTextBox1.Text);
// Clear the input textbox
textBox1.Text = "";
}
}

How to save and load comboBox items consisting of a string and a float value in C# Visual Studio?

I am developing a small C# application that counts the number of single pieces from total weight divided by a reference weight of a single piece.
I want to choose the reference weights from a comboBox. This Reference weights consist of a Name and a float value. I already can add new reference weights to the comboBox and remove them. But I need to know how to save them. I already tried to save it to a file and tried to use application property settings but it didnt work. Could someone show me please the best way to save these comboBox Items? I just want them to be saved when program is closed (save on close) and be loaded on start.
Here is my Code:
// Content item for the combo box
private class Item
{
public string Name;
public string Value;
public Item(string name, string value)
{
Name = name; Value = value;
}
public override string ToString()
{
return Name;
}
}
public Form1()
{
InitializeComponent();
//Combobox Items
comboBox1.Items.Add(new Item("Referenz Gewicht", "1,5"));
comboBox1.Items.Add(new Item("Ticket XYZ", "2,4"));
comboBox1.Items.Add(new Item("Ticket ABC", "0,7"));
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
// Display the Value property
Item itm = (Item)comboBox1.SelectedItem;
Console.WriteLine("{0}, {1}", itm.Name, itm.Value);
//Weist ausgewählten Wert textbox2 zu.
textBox2.Text = itm.Value;
}
private void button2_Click(object sender, EventArgs e)
{
comboBox1.Items.Add(new Item(textBox4.Text, textBox3.Text));
}
private void button1_Click_1(object sender, EventArgs e)
{
comboBox1.Items.Remove(comboBox1.SelectedItem);
}
}
}
To write your items....
StringBuilder sb = new StringBuilder();
foreach(Item itm in combobox1.Items)
{
sb.AppendFormat("{0};{1}{2}", itm.Name, itm.Value, Environment.NewLine);
}
File.WriteAllText("Data.txt", sb.ToString());
To read your items back in the combo
foreach(string line in File.ReadLines("data.txt"))
{
string[] parts = line.Split(';');
Item itm = new Item() { Name = parts[0], Value = parts[1]};
combobox1.Items.Add(itm);
}
Consider also that your 'float' value should be a real float value and not a string property. This will introduce some complexities when you read back the value from file (need a conversion from a string on file to a float in memory) but it is the correct way to do if you are dealing with a floating value number.
A concise version using Linq
Writing:
var text = combobox1.Items.Cast<Item>()
.Select(x => string.Format("{0};{1}",
x.Name, x.Value));
File.WriteAllText(#"data.txt", string.Join(Environment.NewLine, text));
Reading:
var items = File.ReadLines(#"data.txt").Select(x => x.Split(';'))
.Select(k => new Item()
{Name = k[0], Value = k[1]});
combobox1.Items.AddRange(items.ToArray());
(Well it is concise, but I still prefer the verbose version for clarity)

Displaying Multiple Arrays

I have a project where I have to take in input of names, weights, and heights and put them into arrays and display them into a TextBox like this
name = "..."
weight = "..."
height= "..."
I have been able to populate my arrays but I do not understand how to it output it like the example above. Currently my output is all names, THEN all weights then all heights. Could someone explain how I would be able to make it display like the example? The code I have so far is
private void ShowButton_Click(object sender, EventArgs e)
{
txtShow.Text += string.Join(System.Environment.NewLine, nameArray);
txtShow.Text += string.Join(System.Environment.NewLine, weightArray);
txtShow.Text += string.Join(System.Environment.NewLine, heightArray);
}
private void AddButton_Click(object sender, EventArgs e)
{
if (this.index < 10)
{
nameArray[this.index] = nameBox.Text;
weightArray[this.index] = double.Parse(weightBox.Text);
heightArray[this.index] = double.Parse(heightBox.Text);
this.index++;
}
}
The array can store up to 10 values and I am required to use arrays and not lists.
You Should::::: Lots of ways to optimize what you're trying to do, but this is homework and you don't want to look like you're the world's greatest programmer -- you want to do the project like the prof is expecting you to. So creating classes and joining lists is not part of your particular solution set. Try:
PS - on first answer, I tried to keep my suggestion code as close to yours as possible - to answer your question without changing your code. Another commenter suggested that contantly updating a textbox.Text will lead to blinking issues. If that happens to you, I'd suggest using a temporary string as I've edited my text to do.
I know this is homework - so I'm not suggesting any grand optimizations that will make you look like you've been getting your homework done at SO.
EDIT You've asked for a way to detect empty. Based on my understanding of your code and keeping it simple, try:
private void AddButton_Click(object sender, EventArgs e)
{
if (this.index < 10)
{
if(nameBox.Text.Length==0||weightBox.Text.Length==0||heightBox.Text.Length==0){
MessageBox.Show("You must enter a name, weight, and height!");
}else{
nameArray[this.index] = nameBox.Text;
weightArray[this.index] = double.Parse(weightBox.Text);
heightArray[this.index] = double.Parse(heightBox.Text);
this.index++;
nameBox.Text = "";
weightBox.Text = "";
heightBox.Text = "";
}
}
}
private void ShowButton_Click(object sender, EventArgs e)
{ string myString = "";
for(int i=0;i<nameArray.Length;i++)
{
myString+= "Name: "+nameArray[i]+", ";
myString += "Weight: "+weightArray[i]+", ";
myString += "Height: "+heightArray[i]+"\n");
}
txtShow.Text = myString;
}
NOTE Textboxes have validation methods which will do the work of my IF/THEN statement in my revised edit to find empties. If you think the prof is looking for form (control) validation instead of codebehind IF/THEN, let me know and I'll help with that.
Okay - you mentioned the need to sort. To do that, we need to use some way of grouping the input data. We could use Dictionary or class. Let's go with class:
Putting it all together: Have a look at this potential solution - if you think it's too complicated for what your homework should look like, we can try to simplify. Let me know:
public class Person{
public string Name {get;set;}
public double Height {get;set;}
public double Weight {get; set;}
public string Print(){
return "Name: "+Name+", Height: "+Height.ToString()+", Weight: "+Weight.ToString()+"\r\n";
}
}
Person[] People = new Person[10];
int thisIndex = 0;
private void AddButton_Click(object sender, EventArgs e)
{
if (this.index < 10)
{
if(nameBox.Text.Length==0||weightBox.Text.Length==0||heightBox.Text.Length==0)
{
MessageBox.Show("You must enter a name, weight, and height!");
}else{
Person p = new Person();
p.Name = nameBox.Text;
p.Weight = double.Parse(weightBox.Text);
p.Height = double.Parse(heightBox.Text);
People[thisIndex] = p;
thisIndex++;
nameBox.Text = "";
weightBox.Text = "";
heightBox.Text = "";
}
}
}
private void ShowButton_Click(object sender, EventArgs e)
{
People = People.OrderBy(p=>p.Name).ToArray();
string myString = "";
for(int i=0;i<10;i++)
{
if(People[I]!=null){
myString+= People[I].Print();
}
}
txtShow.Text = myString;
}
You should create a class for this purpose. A class enables you to create your own custom types by grouping together variables of three types (string,double and double):
public class Person
{
public string Name { get; set; }
public double Weight { get; set; }
public double Height { get; set; }
public override string ToString()
{
return Name + " " + Weight + " " + Height;
}
}
Then:
Person[] persons = new Person[10];
private void AddButton_Click(object sender, EventArgs e)
{
persons[index] = new Person
{
Name = nameBox.Text,
Weight = double.Parse(weightBox.Text),
Height = double.Parse(heightBox.Text)
};
index++;
}
And finally (look at the ShowButton's code that now is one line):
txtShow.Text += string.Join(System.Environment.NewLine, persons.AsEnumerable());
Linq solution:
private void ShowButton_Click(object sender, EventArgs e) {
int n = Math.Min(Math.Min(nameArray.Length, weightArray.Length), heightArray.Length));
txtShow.Text = String.Join(Environment.NewLine, Enumerable
.Range(0, n)
.Select(i => string.Format("Name: {0}, Weight: {1}, Height: {2}",
nameArray[i], weightArray[i], heightArray[i])));
}
Try avoiding txtShow.Text += fragments, especially in loops (each Textchanging means redrawing and thus blinking)

Conversion program storing information in array and using a combobox

This is homework (I always try to point that out so it's up front). The program is a conversion program. The user picks a conversion option from a combo box and then enters a length (ie: Feet to meters) when the user hits calculate the program calculates the conversion. I have several questions because the array part is confusing me. I wanted to make sure I am going in the right direction.
I think I am using my array to populate my combo box (I used an example provided although I do not fully understand it). When the user hits the calculate button should I be storing my conversion values in the same array? something like:
string [,] conversions = { {kilometers to miles, .11111}, {miles to kilometers, .11111}}
Or am I heading in the right direction with what I have? To be clear as it is coded the array populates my combobox so if I add the extra data then it will display those numbers in the combobox, which isn't really what I am going for.
My next question, when the user hits calculate button how is it going to know what option the user has selected? I think it has something to do with index but I'm confused as far as what is actually declaring it?
*****Disregard this question. I think I found an answer*********
Answer on Updating Labels
Finally I think my last question is the page has labels next to the textboxes so if the user chooses 'Miles to kilometers" the entry textbox is going to say Miles and then the answer textbox would say Kilometers... What is that called? I need to find it in my book and cannot. I know I'm missing something but I'm trying to find either an example or where in the book it was covered and I'm simply not seeing it.
Below is my code that I currently have.
public partial class FrmConversions : Form
{
const double Miles_To_Kilometers = 1.6093;
const double Kilometers_To_Miles = 0.6214;
const double Feet_To_Meters = 0.3048;
const double Meters_To_Feet = 3.2808;
const double Inches_To_Centimeters = 2.54;
const double Centimeters_To_Inches = 0.3937;
public FrmConversions()
{
InitializeComponent();
}
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
private void FrmConversions_Load(object sender, EventArgs e)
{
cboConversions.Items.Clear(); //clear the combobox
string[,] conversions =
{
{"Kilometers to Miles" , "Miles to Kilometers"},
{"Feet to Meters" , "Meters to Feet"},
{"Inches to Centimeters", "Centimeters to Inches"}
};
foreach (string str in conversions)
{
cboConversions.Items.Add(str);
}
}
private void btnClear_Click(object sender, EventArgs e)
{
txtEntry.Clear();
txtAnswer.Clear();
}
public bool IsDecimal(TextBox txtEntry, string name)
{
try
{
Convert.ToDecimal(txtEntry.Text);
return true;
}
catch (FormatException)
{
MessageBox.Show(name + " must be a decimal value.", "Entry Error");
txtEntry.Focus();
return false;
}
}
private void btnCalculate_Click(object sender, EventArgs e)
{
int index = cboConversions.SelectedIndex;
if (index != -1)
{
try
{
if (IsDecimal())
{
txtAnswer.Text = ToString;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n\n" +
ex.GetType().ToString() + "\n" +
ex.StackTrace, "exception");
}
}
}
}
}
}
There are many ways to do this, a Dictionary would be neat?
In this example, the Keys of the dictionary (the strings I specified) are loaded into the combo box. Later, those strings can be used to retrieve the conversion value from the dictionary.
// Declare at the top
Dictionary<string, double> conversions = {
{ "Kilometers to Miles", Kilometers_To_Miles},
{ "Miles to Kilometers", 1 / Kilometers_To_Miles},
{ "Feet to Meters", Feet_To_Meters},
{ "Meters to Feet", 1 / Feet_To_Meters},
etc
};
// In Form Load
foreach (string str in conversions.Keys)
{
cboConversions.Items.Add(str);
}
// In btnCalculate_Click
var conversionValue = conversions[cboConversions.Text];
var convertedValue = (double)txtEntry.Text * conversionValue; // Need to validate is numeric
txtAnswer.Text = convertedValue
If you were hellbent on using arrays then this would work too, but cheats by using LINQ at the end:
// Declare at the top
object[,] conversions = {
{ "Kilometers to Miles", Kilometers_To_Miles},
{ "Miles to Kilometers", 1 / Kilometers_To_Miles},
{ "Feet to Meters", Feet_To_Meters},
{ "Meters to Feet", 1 / Feet_To_Meters},
etc
};
// In Form Load
foreach (string str in conversions)
{
cboConversions.Items.Add(str[0]);
}
// In btnCalculate_Click
var conversionValue = conversions.First(x => x[0] == cboConventions.Text)[1];
var convertedValue = (double)txtEntry.Text * conversionValue; // Need to validate is numeric
txtAnswer.Text = convertedValue

Categories