My partial array keeps adding the empty data C# - c#

I'm new to C# and we need to create an array that can hold 100 scores. But it needs to also accept something smaller (txt file with numbers between 0-100). My issue is that even tho I add a txt file that has 50 scores, it will think there is another 50 'blank scores' and it will count those as 0. So when my code does the average, total, and lowest score, it will mess it up. My textbook isn't being very help with the answer.
private double Total(double[] iArray)
{
double total = 0;
total = iArray.Length;
return total;
}
//Average test score
private double Average(double[] iArray)
{
double total = 0;
double average;
for (int index = 0; index < iArray.Length; index++)
{
total += iArray[index];//look into later
}
average = (double)total / iArray.Length;
return average;
} //done
//for hightest test score
private double Highest(double[] iArray)
{
double highest = iArray[0];
for (int index = 1; index < iArray.Length; index++)
{
if (iArray[index] > highest)
{
highest = iArray[index];
}
}
return highest;
} //done
private double Lowest(double[] iArray)
{
double lowest = iArray[0];
for (int index = 1; index < iArray.Length; index++)
{
if (iArray[index] < lowest)
{
lowest = iArray[index];
}
}
return lowest;
}//done
private void addFileButton_Click(object sender, EventArgs e)
{
try
{
const int SIZE = 100; //number of test
double[] scores = new Double[SIZE]; //array of the test scores
int index = 0; //loop counter
int count = 0;
double highestScore;
double lowestScore;
double averageScore;
double totalScore;
//Asking user to open a file
StreamReader inputFile;
if (openFile.ShowDialog() == DialogResult.OK)
{
inputFile = File.OpenText(openFile.FileName);
while (!inputFile.EndOfStream && count < scores.Length)//switching index to count
{
scores[count] = int.Parse(inputFile.ReadLine());
count++;
}
inputFile.Close();
}
else
{
MessageBox.Show("Operation Canceled.");
}
//Display test scores
for (index = 0; index < count; index++)
{
scoreListBox.Items.Add(scores[index].ToString());
}
//grabbing information
highestScore = Highest(scores);
lowestScore = Lowest(scores);
averageScore = Average(scores);
totalScore = Total(scores);
//display values
highesScoreLabel.Text = highestScore.ToString();
lowestScoreLabel.Text = lowestScore.ToString();
averageTestScoreLabel.Text = averageScore.ToString();
totalTestScoresLabel.Text = totalScore.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

You might just have to use Array.Resize then:
while (!inputFile.EndOfStream && scores.Count <= SIZE)
{
scores[count] = int.Parse(inputFile.ReadLine());
count++;
}
Array.Resize(scores, --count);
See if that works.
This was suggested before the OP's comment clarifying the should specifically use an array:
What if you used a List<double> when collecting the scores, then .ToArray() before you pass it into your other methods?
const int SIZE = 100;
List<double> scores = new List<double>();
[...]
if (openFile.ShowDialog() == DialogResult.OK)
{
inputFile = File.OpenText(openFile.FileName);
while (!inputFile.EndOfStream && scores.Count <= SIZE)
{
scores.Add(int.Parse(inputFile.ReadLine()));
count++;
}
[...]
var arrScores = scores.ToArray();
//grabbing information
highestScore = Highest(arrScores);
lowestScore = Lowest(arrScores);
averageScore = Average(arrScores);
totalScore = Total(arrScores);
Notice that the while loop has the condition changed for scores.Count <= SIZE to still only allow up to 100 scores.

Instead of constructing the Array yourself and implementing all these methods, I suggest, you read up in LINQ. LINQ stands for Language INtegrated Query and is essentially a bunch of extension methods on IEnumerable<T> that provide all of the functionality you need here.
I rewrote your event handler to use LINQ and it became much simpler.
private void addFileButton_Click(object sender, EventArgs e)
{
try
{
// Easier to follow if we just exit early
if (openFile.ShowDialog() != DialogResult.OK)
{
MessageBox.Show("Operation Canceled.");
return;
}
var scores = File.ReadAllLines(openFile.FileName)
// Always use TryParse to avoid exceptions
.Select(l => int.TryParse(l, out var score) ? score : -1)
// Filter out everything that is no valid score
.Where(s => s >= 0)
// You could also use ToList() here for a possible minimal performance gain and the possibility to add items later.
.ToArray();
// Display test scores
// We are using foreach instead of for here because we do not care about indexes. We simply want to add each item.
// We also do not need to add the ToString(), just adding an int is fine.
foreach (var score in scores)
scoreListBox.Items.Add(score);
// grabbing information
// We use LINQ methods here. Microsoft was nice enough di implement a bunch of often used methods on collections for us.
var highestScore = scores.Max();
var lowestScore = scores.Min();
var averageScore = scores.Average();
var totalScore = scores.Sum();
//display values
highesScoreLabel.Text = highestScore.ToString();
lowestScoreLabel.Text = lowestScore.ToString();
averageTestScoreLabel.Text = averageScore.ToString();
totalTestScoresLabel.Text = totalScore.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

Related

Windows form - Make array to stop when there is no more data

Hello guys I am new to coding, and new to arrays as well and I was wondering how can I make this code to stop when there is no more names in the array, and from stop saying the number 10 always before the names. As well to show the position of the names one number above, not just starting from 0, but from 1.
Here is what I have so far.
string[] list = new string[10];
int pos = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string StudentName;
StudentName = textBox1.Text;
list[pos] = StudentName;
pos++;
textBox1.Text = "";
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show(list.GetLength(0).ToString());
string message;
for (int i = 0; i < lista.GetLength(0); i++)
{
message = "";
message = "The student" + " " + list[i] + " has the position " + i ;
MessageBox.Show(message);
}
}
Use a dynamic array, aka List<T>
List<string> list = new List<string>();
Instead of manually keeping track of the position, just add the item to the end:
list.Add(StudentName);
You can check the number of current items in the list by using .Count.
for (int i = 0; i < list.Count; i++) {...}
But you can also use a foreach loop to iterate over all the items in the list, but this way you do not get the position automatically.
foreach(var item in list){...}
For the sake of completeness, another way would be to filter out empty entries.
Linq is especially great for working with Enumerables like these:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.where?view=net-6.0
using System;
using System.Linq;
public class Program
{
public static void Main()
{
string[] list = new string[10];
list[0] = "test1";
list[1] = "test2";
for (var index = 0; index < list.Length; index++)
{
var s = list[index];
Console.WriteLine($"[{index}] {s}");
}
// Filter out null, an empty string or only whitespace characters from the array
list = list
.Where(c => !string.IsNullOrWhiteSpace(c))
.ToArray();
Console.WriteLine("==========================");
for (var index = 0; index < list.Length; index++)
{
var s = list[index];
Console.WriteLine($"[{index}] {s}");
}
}
}
https://dotnetfiddle.net/g7e0Nm
First you can specify the maximum capacity of your array using a const
const int arrayCapacity = 10;
string[] list = new string[arrayCapacity];
int pos = 0;
Then you should add some validation if the array is full, according to your maximum capacity, and/or if the textbox is empty.
private void button1_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(textBox1.Text))
{
MessageBox.Show("Textbox was empty");
return;
}
if (pos > arrayCapacity - 1)
{
MessageBox.Show("Array is full");
textBox1.Clear();
return;
}
list[pos] = textBox1.Text;
pos++;
textBox1.Clear();
}
And the last step is to create a new array in any case you have a smaller number of students than your initial array's capacity
private void button2_Click(object sender, EventArgs e)
{
var arr = list.Where(x => !string.IsNullOrEmpty(x)).ToArray();
for (int i = 0; i < arr.Length; i++)
{
string message = "The student" + " " + arr[i] + " has the position " + (i + 1);
MessageBox.Show(message);
}
}

Generate random number with quantity

I have questions how can I set the quantity of numbers in the result box based on the selected value in dropdown
This is my code
if (!string.IsNullOrEmpty(TextBox1.Text) && !string.IsNullOrEmpty(TextBox2.Text))
{
string totalListNumber = "";
for (double x = Convert.ToDouble(TextBox1.Text); x <= Convert.ToDouble(TextBox2.Text); x++)
{
totalListNumber += x + "\n";
}
TextBox3.Text = totalListNumber;
}
Firstly you need to create a random number between your two values.
System.Random roll = new Random();
int nextRandom = roll.Next(minValue, maxValue);
Your combo box has an event called SelectedIndexChanged that will trigger when the user changes your combo selection.
In your SelectedIndexChanged code you should then put something like:
private void cmbQuantities_SelectedIndexChanged(object sender, EventArgs e)
{
System.Random roll = new Random();
int nextRandom;
// turn the two textBoxes into two doubles minValue and maxValue
if ((double.TryParse(TextBox1.Text, out double minValue) == false) || (double.TryParse(TextBox2.Text, out double maxValue) == false)) return;
// get the number of items to display or exit if it is not a valid number (return)
if (int.TryParse(cmbQuantities.Text, out int randomsToDisplay) == false) return;
string totalListNumber = String.Empty;
for(int i = 0; i < randomsToDisplay; i++)
{
nextRandom = roll.Next(minValue, maxValue);
totalListNumber += nextRandom.ToString() + "\n";
}
TextBox3.Text = totalListNumber;
}
There are a lot of other boosts you should do (such as using a listView etc), but this will get you what you want.
You should also try looking at using a datasource for the comboBoxQuantity that has an int value and string description so that selecting the combo item gets you the quantity value as an int automatically (or even just use a textbox!)
Avoiding duplicate numbers
As the Random class will produce duplicate numbers you will need to store your random rolls in a list and then check the list every time a new number is rolled.
The HashSet class allows fast lookups by a key (your random number in this case).
So:
private void cmbQuantities_SelectedIndexChanged(object sender, EventArgs e)
{
System.Random roll = new Random();
int nextRandom;
HashSet<int> rolledRandoms = new HashSet<int>();
// turn the two textBoxes into two doubles minValue and maxValue
if ((double.TryParse(TextBox1.Text, out double minValue) == false) || (double.TryParse(TextBox2.Text, out double maxValue) == false)) return;
// get the number of items to display or exit if it is not a valid number (return)
if (int.TryParse(cmbQuantities.Text, out int randomsToDisplay) == false) return;
string totalListNumber = String.Empty;
for(int i = 0; i < randomsToDisplay; i++)
{
nextRandom = roll.Next(minValue, maxValue);
// keep rolling until the nextRandom value is not in the rolledRandoms list
while (rolledRandoms.ContainsKey(nextRandom))
nextRandom = roll.Next(minValue, maxValue);
rolledRandoms.Add(nextRandom);
totalListNumber += nextRandom.ToString() + "\n";
}
TextBox3.Text = totalListNumber;
}

How to calculate the "average" value , "highest" value , and "Lowest" value in an array? C#

PART 1: I am to create a program that that reads a file’s contents into an array, and displays the array’s contents in a ListBox control, and calculates and displays the total of the array’s values. - DONE THAT PART
PART 2: Calculate the average, Highest, and Lowest value and display them in a Label control.
I'm new to coding so I couldn't do much, I turn to stack overflow for help
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace SalesAnalysis
{
public partial class SalesAnalysisApplication : Form
{
public SalesAnalysisApplication()
{
InitializeComponent();
}
private void salesAnalysisButton_Click(object sender, EventArgs e)
{
//declaring array
const int SIZE = 100;
decimal[] sales = new decimal[SIZE];
//varible to hold amount stored in array
int count = 0;
//declaring streamreader
StreamReader inputFile;
//opening the sales file
inputFile = File.OpenText("Sales.txt");
try
{
//pull contents from file into array while there is still
// items to pull and the array isnt full
while (!inputFile.EndOfStream && count < sales.Length)
{
sales[count] = decimal.Parse(inputFile.ReadLine());
count++;
}
//close the file
inputFile.Close();
//display contents in listbox
for (int index = 0; index < count; index++)
{
salesListBox.Items.Add(sales[index]);
}
//Calculate the sum of all values
for (int index = 0; index < sales.Length; index++)
{
totalSales += sales[index];
}
//display total of all values
salesListBox.Items.Add("Total =" + totalSales);
//Determine the average sales from the array
for (int index = 0; index < sales.Length; index++)
{
//calculate the average
averageSales = totalSales / 7;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Clear_Click(object sender, EventArgs e)
{
//Clear all fields
salesListBox.Items.Clear();
averageResultsLabel.Text = "";
highestResultsLabel.Text = "";
lowestResultsLabel.Text = "";
}
private void exitButton_Click(object sender, EventArgs e)
{
//close form
this.Close();
}
}
}
You can use linq to do this for example:
var list=Enumerable.Range(0,1000);
var average = list.Average();
var highest = list.Max()
var smallest= list.Min()
var total= list.Sum()
P.S do not forget to add using System.Linq;
The non-linq approach.
You have a sales.Length and a totalSales. Therefore, you have an averageSales.
For max & min, while you are in the for loop
for (int index = 0; index < sales.Length; index ++
{
if (sales
}
simply assign the value based on an if statement. Something like:
if (sales[index] > highestSales)
{
highestSales = sales[index];
}
if (sales[index] < lowestSales)
{
lowestSales = sales[index];
}
First convert u r Decimal array to List.
List<decimal> lst = sales.OfType<decimal>().ToList(); // Convert to List.
Then Follow Linq Operations.
lst.Average(); lst.Min();
In case you prefer good old loop (and no Linq), you can try File.ReadLines and foreach:
decimal max = 0;
decimal min = 0;
decimal sum = 0;
int count = 0;
foreach(string line in File.ReadLines("Sales.txt")) {
decimal value = decimal.Parse(line);
salesListBox.Items.Add(value);
if (count == 0 || value > max)
max = value;
if (count == 0 || value < min)
min = value;
sum += value;
count += 1;
}
decimal average = sum / count;
averageResultsLabel.Text = average.ToString("f2");
highestResultsLabel.Text = max.ToString();
lowestResultsLabel.Text = min.ToString();
Here we don't have to create any array at all. If you insist on having array a simple Linq can help
decimal[] sales = File
.ReadLines("Sales.txt")
.Select(line => decimal.Parse(line))
.ToArray();
And foreach will be
foreach(decimal value in sales) {
salesListBox.Items.Add(value);
...
}

How can the anagram code be improved and made faster?

The program runs slowly when there are more than 10 characters, I would like to do it quickly. I do not understand how I can improve and ask for your help.
And still need to count and number those combinations that do not occur in their original positions, I think if I add this logic, then the program will work even longer, and this also requires your advice
P.S. sorry for my English)
private void btnGo_Click(object sender, EventArgs e)
{
this.Size = new Size(632, 430);
button1.Visible = true;
// Get the items.
string[] items = txtItems.Text.Split(' ');
// string[] items = txtItems.Text.ToString;
// Generate the permutations.
List<List<string>> results =
GeneratePermutations<string>(items.ToList());
// results.Where(x => !HasCharacterAtSamePositionAsInOriginal(items, x));
List<string> list = new List<string>();
// Display the results.
lstPermutations.Items.Clear();
foreach (List<string> combination in results.Where(x => !HasCharacterAtSamePositionAsInOriginal(items, x)))
{
if (checkBox1.Checked == true)
{
lstPermutations.Items.Add(string.Join("", combination.ToArray()));
}
else
{
lstPermutations.Items.Add(string.Join(" ", combination.ToArray()));
}
}
// Calculate the number of permutations.
long num_permutations = Factorial(items.Length);
txtNumPermutations.Text = num_permutations.ToString();
// Check the result.
// Debug.Assert(lstPermutations.Items.Count == num_permutations);
}
private List<List<T>> GeneratePermutations<T>(List<T> items)
{
// Make an array to hold the
// permutation we are building.
T[] current_permutation = new T[items.Count];
// Make an array to tell whether
// an item is in the current selection.
bool[] in_selection = new bool[items.Count];
// Make a result list.
List<List<T>> results = new List<List<T>>();
// Build the combinations recursively.
PermuteItems<T>(items, in_selection,
current_permutation, results, 0);
// Return the results.
return results;
}
// Recursively permute the items that are
// not yet in the current selection.
private void PermuteItems<T>(List<T> items, bool[] in_selection,
T[] current_permutation, List<List<T>> results, int next_position)
{
// See if all of the positions are filled.
if (next_position == items.Count)
{
// All of the positioned are filled.
// Save this permutation.
results.Add(current_permutation.ToList());
}
else
{
// Try options for the next position.
for (int i = 0; i < items.Count; i++)
{
if (!in_selection[i])
{
// Add this item to the current permutation.
in_selection[i] = true;
current_permutation[next_position] = items[i];
// Recursively fill the remaining positions.
PermuteItems<T>(items, in_selection,
current_permutation, results, next_position + 1);
// Remove the item from the current permutation.
in_selection[i] = false;
}
}
}
}
// Return n!
private long Factorial(long n)
{
long result = 1;
for (int i = 2; i <= n; i++) result *= i;
return result;
}
bool HasCharacterAtSamePositionAsInOriginal(string [] originalWord, List<string> anagram) //check the symbol occurs in its original location or not
{
for (var i = 0; i < originalWord.Length; i++)
{
if (originalWord[i].Equals(anagram[i]))
{
return true;
}
}
return false;
}

How to display array elements, if the number of elements is a variable

I'm making a calculator in GUI, and I need some help.
When I enter some data in a text box, I need to store it in an array. This is how I thought of it.
int numOfPackages;//used to get user input
private void button3_Click(object sender, EventArgs e)
{
int[] weight = new int[numOfPackages];
for(int i = 0; i < numOfPackages; i++)
{
weight[i] = Convert.ToInt32(weightBox.Text);
}
foreach (int i in weight)
totalCostLabel.Text = "" + weight[i];
}
And when I try to display the elements, it gives me the indexOutOfRange exception.
So, how do I display the elements of that array?
Thanks in advance.
This line
foreach (int i in weight)
totalCostLabel.Text = "" + weight[i];
should be
foreach (int w in weight)
totalCostLabel.Text = "" + w;
Your current code iterates the array of weights, and tries to use the weight as an index into the array of weights, causing an index out of range exception.
Another problem is with the first loop: you are setting all values of weight to the same number:
weight[i] = Convert.ToInt32(weightBox.Text); // That's the same for all i-s
If weights are to be different, they should come from different weight boxes, or the string from a single weightBox should be processed in such a way as to produce multiple numbers (for example, by using string.Split).
You have multiple problems here. First is this:
foreach (int i in weight)
totalCostLabel.Text = "" + weight[i];
This is iterating the weight array and using each value in that array. You then use that value as an index. Take the following example:
weight[0] = 0
weight[1] = 1
weight[2] = 15
In your code, the first two entries will work because there is an index of 0 and an index of 1. But when it gets to the last entry, it looks for an index of 15. You can fix this two ways, the first is to use a regular for loop:
for(int i=0; i < weight.Length; i++)
{
totalCostLabel.Text += weight[i];
}
This brings the second mistake. You aren't appending anything to your totalCostLabel in your code, you are just replacing the value. This will append all the values of weight together as one.
Another way to do this is to use the foreach loop:
foreach(int i in weight)
{
totalCostLabel.Text += i;
}
This is the same as above but you don't have to worry about indexing.
Bottom line, even after you fix your loop, you will probably need to fix the way that the label takes the text otherwise you won't get your desired result.
Maybe you wanted something more like?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
btnAdd.Enabled = false;
}
int[] weight;
int entriesMade;
int numOfPackages;
private void btnReset_Click(object sender, EventArgs e)
{
if (int.TryParse(numEntriesBox.Text, out numOfPackages))
{
weight = new int[numOfPackages];
entriesMade = 0;
btnReset.Enabled = false;
btnAdd.Enabled = true;
totalCostLabel.Text = "";
}
else
{
MessageBox.Show("Invalid Number of Entries");
}
}
private void btnAdd_Click(object sender, EventArgs e)
{
int value;
if (int.TryParse(weightBox.Text, out value))
{
weight[entriesMade] = value;
weightBox.Clear();
totalCostLabel.Text = "";
int total = 0;
for (int i = 0; i <= entriesMade; i++)
{
total = total + weight[i];
if (i == 0)
{
totalCostLabel.Text = weight[i].ToString();
}
else
{
totalCostLabel.Text += " + " + weight[i].ToString();
}
}
totalCostLabel.Text += " = " + total.ToString();
entriesMade++;
if (entriesMade == numOfPackages)
{
btnAdd.Enabled = false;
btnReset.Enabled = true;
MessageBox.Show("Done!");
}
}
else
{
MessageBox.Show("Invalid Weight");
}
}
}

Categories

Resources