Issue with bucket sort for strings C# - c#

I am writing a program that takes in data for student info with a student ID # field, student first name field and student last name field. The user will input the data for each student (up to 20 students) or until the user enters '999' for the student ID field. Next I would like to sort the student info into two separate buckets based on the last name field.
I am having an issue with the buckets separating correctly. I will use the CompareTo method to compare the last name strings for the student last name array but when I print the buckets they are mixed up. For example, the last names starting with A-K should go into a 'low values' bucket and last names starting with J-Z should go into a 'high values' bucket.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _123_Assignment2
{
using System;
using static System.Console;
class Program
{
struct student
{
public int studentId;
public string firstName;
public string lastName;
};
static void Main(string[] args)
{
student[] studentInfo = new student[20];
string[] bucketLow = new string[20];
string[] bucketHigh = new string [20];
int x = 0;
int y = 0;
int z = 1;
WriteLine("Enter student ID number:");
studentInfo[x].studentId = Convert.ToInt32(ReadLine());
while (studentInfo[x].studentId != 999)
{
WriteLine("Enter first name:");
studentInfo[x].firstName = ReadLine();
WriteLine("Enter last name:");
studentInfo[x].lastName = ReadLine();
x++;
WriteLine("Enter student ID number:");
studentInfo[x].studentId = Convert.ToInt32(ReadLine());
}
for (int j = 0; j < x; j++)
{
if(studentInfo[j].lastName.CompareTo(studentInfo[z].lastName)<= 0)
bucketLow[y] = studentInfo[j].lastName;
else
bucketHigh[y] = studentInfo[j].lastName;
y++;
z++;
}
WriteLine("Unsorted Table:");
for (int j = 0; j < studentInfo.Length; j++)
{
WriteLine("{0}{1}{2}",studentInfo[j].studentId,studentInfo[j].firstName,
studentInfo[j].lastName);
}
WriteLine("Bucket 1:");
for (int j = 0; j < x; j++)
{
WriteLine(bucketLow[j]);
}
WriteLine("Bucket 2:");
for (int j = 0; j < x; j++)
{
WriteLine(bucketHigh[j]);
}
}
}
}
I believe I am not writing the CompareTo method correctly, I tried sorting from both the beginning and end of the array respectively and keep getting the same results?

As this appears to be homework, I'll refrain from actually writing the correct code for you. But here are at least some of your problems:
Directly addressing your concern, the code you have to sort the array elements is using the wrong comparison. If you have two buckets, and you want one to represent last names starting with A through K and the other to represent last names starting with L through Z, then you need to compare to K or L to determine the right bucket. Comparing to other names is just going to randomize the data. Something like string.Compare(studentInfo[j].lastName, "L", StringComparison.CurrentCultureIgnoreCase) < 0 should work.
You need to maintain two indexes, one for each bucket, and only increment an index for that bucket if you've actually copied a student record to that bucket.
Your current code will crash with an IndexOutOfRangeException if you actually try to entry data for 20 students, because you increment x and store the ID value into the array without checking to see if you've entered 20 student's worth of data yet. After 20 students have been entered, then even if the user enters 999, the while condition won't check that until it's too late and the code has already tried to stored the value into the array.
There may be other problems; those are the ones that I noticed at first glance.
For future reference, you should make sure when asking questions here on Stack Overflow that you provide a good Minimal, Complete, and Verifiable code example. You came close; at least the code was complete. But don't make other SO users enter your test data. Write a separate program that includes all the data built-in, doesn't have user prompts, and doesn't do anything that isn't strictly required to reproduce the issue you're having.

I do not think that you sorting works (the j and z comparisons):
studentInfo[j].lastName.CompareTo(studentInfo[z].lastName)<= 0
Try to simplify your bucket sort - if you know that your buckets are A-K and J-Z maybe you should replace the part of:
for (int j = 0; j < x; j++)
{
if(studentInfo[j].lastName.CompareTo(studentInfo[z].lastName)<= 0)
bucketLow[y] = studentInfo[j].lastName;
else
bucketHigh[y] = studentInfo[j].lastName;
y++;
z++;
}
}
Try something like:
for (var i = 0; i < studentInfo.Length; i++)
{
if (studentInfo[i].lastName[0] <= 'K')
bucketLow[y] = studentInfo[i].lastName;
else
bucketHigh[y] = studentInfo[i].lastName;
y++;
}
(And of course add some check that you have a valid input of at least 1 character and so on...)

If you must use a struct and arrays then you may consider the code below for separating the names into the appropriate buckets. As I commented you need to two indexes, one for each bucket. Because you are fixing the array sizes to 20, there will be empty rows when outputting the results if there are less than 20.
x = studentInfo.Length;
int lowIndex = 0;
int highIndex = 0;
for (int j = 0; j < x; j++) {
if (String.CompareOrdinal(studentInfo[j].lastName, "L") < 0) {
bucketLow[lowIndex] = studentInfo[j].lastName;
lowIndex++;
} else {
bucketHigh[highIndex] = studentInfo[j].lastName;
highIndex++;
}
}

Related

How can I detect if there are duplicates in a C# array and can I replace them with another suitable value in the same place in the array?

I'm kind of new to C# and I'm trying to make a sudoku game. I'm using a for loop to loop through the column arrays and add a random number to the spots but I'm using Random and Next() so it allows the numbers to repeat. But because I can't have more than one of a number in each column (array) for it to function, how can I replace the repeated number with another number that isn't repeated? I don't know how to do this.
Also the columns are stored in other arrays that I've called rows just so that I can say Row1[0][2] to access position 3 in column 1 for example.
Here's the method & for loop I'm using to replace add the numbers to the arrays:
void populateColumns(int[][] arr) // goes through column arrays and replaces the numbers with random ones from Num()
{
int i;
int j = 0;
for (int l = 0; l <= 8; l++)
{
for (i = 0; i <= 8; i++)
{
arr[j][i] = Num();
int currentPos = arr[j][i];
Console.Write(currentPos);
}
j = l;
}
}

Simple C# program using arrays throws an error

Well I'm trying to write a program in which if you add for example 3 integers in the array, let's say 3 2 1, it will add them again after it so it becomes 321 321.
Here is the code I need to fix. And sorry for the stupid question I am a beginner with arrays.
I get this error
Index was outside the bounds of the array
My code:
using System;
public class Program
{
public static void Main()
{
int arraylength = int.Parse(Console.ReadLine());
int[] array = new int[arraylength];
for (int i = 0; i < arraylength + 1 / 2; i++)
{
int typed = int.Parse(Console.ReadLine());
array[i] = typed;
if (i == arraylength / 2)
{
for (int a = arraylength + 1 / 2; a < arraylength + 1; a++)
{
array[a] = typed;
}
}
}
}
}
Array indices in C# start at 0 and end at length - 1. You need to remove the + 1 from each of your for loop conditions:
for (int i = 0; i < arraylenght / 2; i++)
and
for (int a = (arraylenght + 1) / 2; a < arraylenght; a++)
I also suggest that you change arraylenght to arraylength. Since you probably autocompleted this every time you used it, the misspelling occurs consistently throughout your code and the compiler is satisfied. However, misspellings make it difficult for humans to read your code.
p.s. Your code doesn't do what you think it does. I suggest you step away from the computer for a moment and write in words what you are trying to accomplish. Describe each step of your solution in as much detail as you can. Then look at how your words match with the code you wrote. You will probably find that you do not need nested loops.

Getting the column totals in a 2D array but it always throws FormatException using C#

I am planning to get an array of the averages of each column.
But my app crashes at sum[j] += int.Parse(csvArray[i,j]); due to a FormatException. I have tried using Convert.ToDouble and Double.Parse but it still throws that exception.
The increments in the for loop start at 1 because Row 0 and Column 0 of the CSV array are strings (names and timestamps). For the divisor or total count of the fields that have values per column, I only count the fields that are not BLANK, hence the IF statement. I think I need help at handling the exception.
Below is the my existing for the method of getting the averages.
public void getColumnAverages(string filePath)
{
int col = colCount(filePath);
int row = rowCount(filePath);
string[,] csvArray = csvToArray(filePath);
int[] count = new int[col];
int[] sum = new int[col];
double[] average = new double[col];
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
if (csvArray[i,j] != " ")
{
sum[j] += int.Parse(csvArray[i,j]);
count[j]++;
}
}
}
for (int i = 0; i < average.Length; i++)
{
average[i] = (sum[i] + 0.0) / count[i];
}
foreach(double d in average)
{
System.Diagnostics.Debug.Write(d);
}
}
}
I have uploaded the CSV file that I use when I test the prototype. It has BLANK values on some columns. Was my existing IF statement unable to handle that case?
There are also entries like this 1.324556-e09due to the number of decimals I think. I guess I have to trim it in the csvToArray(filePath) method or are there other efficient ways? Thanks a million!
So there are a few problems with your code. The main reason for your format exception is that after looking at your CSV file your numbers are surrounded by quotes. Now I can't see from your code exactly how you convert your CSV file to an array but I'm guessing that you don't clear these out - I didn't when I first ran with your CSV and experienced the exact same error.
I then ran into an error because some of the values in your CSV are decimal, so the datatype int can't be used. I'm assuming that you still want the averages of these columns so in my slightly revised verion of your method I change the arrays used to be of type double.
AS #musefan suggested, I have also changed the check for empty places to use the IsNullOrWhiteSpace method.
Finally when you output your results you receive a NaN for the first value in the averages column, this is because when you don't take into account that you never populate the first position of your arrays so as not to process the string values. I'm unsure how you'd best like to correct this behaviour as I'm not sure of the intended purpose - this might be okay - so I've not made any changes to this for the moment, pop a mention in the comments if you want help on how to sort this!
So here is the updated method:
public static void getColumnAverages(string filePath)
{
// Differs from the current implementation, reads a file in as text and
// splits by a defined delim into an array
var filePaths = #"C:\test.csv";
var csvArray = File.ReadLines(filePaths).Select(x => x.Split(',')).ToArray();
// Differs from the current implementation
var col = csvArray[0].Length;
var row = csvArray.Length;
// Update variables to use doubles
double[] count = new double[col];
double[] sum = new double[col];
double[] average = new double[col];
Console.WriteLine("Started");
for (int i = 1; i < row; i++)
{
for (int j = 1; j < col; j++)
{
// Remove the quotes from your array
var current = csvArray[i][j].Replace("\"", "");
// Added the Method IsNullOrWhiteSpace
if (!string.IsNullOrWhiteSpace(current))
{
// Parse as double not int to account for dec. values
sum[j] += double.Parse(current);
count[j]++;
}
}
}
for (int i = 0; i < average.Length; i++)
{
average[i] = (sum[i] + 0.0) / count[i];
}
foreach (double d in average)
{
System.Diagnostics.Debug.Write(d + "\n");
}
}

Printing a sorted array

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SkihopperOpgave
{
class Program
{
static void Main(string[] args)
{
string deltagerNavn = " ";
int hopLængde = 0;
string[] deltager = new string[5];
int[] hopLængder = new int[5];
string[] pladsNumre = new string[5] { "1. ", "2. ", "3. ", "4. ", "5. " };
for (int i = 0; i < deltager.Length; i++)
{
Console.Write("Indtast deltagernavn: ");
deltager[i] = Console.ReadLine();
Console.Write("Indtast hoplængde for deltager: ");
hopLængder[i] = Convert.ToInt32(Console.ReadLine());
for (int j = hopLængder.Length-1; j>0; j--) //Bubblesort
{
for (int k = 0; k < j; k++)
{
int b = hopLængder[k];
hopLængder[k] = hopLængder[k+1];
hopLængder[k+1] = b;
}
}
for (int s = 0; s < deltager.Length; s++)
{
Console.WriteLine(pladsNumre[s] + hopLængder[s] + deltager[s]);
}
}
}
}
}
I am trying to create a program that prints out a sorted list of Ski jumpers (deltagere), by sorting their jump distance (hoplængde). What happens is that the user first types in the name of the ski jumper, then the jump distance. The program should then print out a sorted list in this format: 1. 80 John (Newline) 2. 45 Mark... etc.
Each time a user types in a ski jumper, the list should be printed, with all the distances sorted in a descending order.
I've created an array for both names of the ski jumpers and their distances, but I am having trouble as to how I connect the first element in the int array with the first element in the string array and how to correctly sort this in each iteration.
I tried creating a bubblesort for the job, but I get wrong results.
Take a look at overloads of static method Array.Sort() - it can do what you want.
Also you should read about SortedList<> class - it is probably even better option for your task and use comments already posted (by Fischerman) to declare an entity class.
I think that's enough hints for a homework =)

How to write groups of numbers using Console.Write?

I'm very new to C# (And Stack Overflow, forgive me for any poor etiquette here), and I'm writing the game Mastermind in a console application. I'm trying to show a list of the user's guesses at the end of the game, and I know that using Console.WriteLine(); will just give me 30-odd lines off numbers which don't tell the user anything.
How can I alter my code so that the program displays 4 numbers in a group, at a time? For example:
1234
1234
1234
//Store numbers in a history list
ArrayList guesses = new ArrayList(); //This is the ArrayList
Console.WriteLine("Please enter your first guess.");
guess1 = Convert.ToInt32(Console.ReadLine());
guesses.Add(guess1);
foreach (int i in guesses)
{
Console.Write(i);
}
I assume that each element of your byte array is a single digit (0-9). If that assumption is invalid -- please let me know, I'll modify the code :)
Action<IEnumerable<int>> dump = null;
dump = items =>
{
if(items.Any())
{
var head = String.Join("", items.Take(4));
Console.WriteLine(head);
var tail = items.Skip(4);
dump(tail);
}
};
dump(guesses);
It looks like you're most of the way there, you have a console write that writes them all out without linebreaks. Next add an integer count and set it to zero. Increment it by one in the foreach loop. count % 4 == 0 will then be true for all counts that are a multiple of four. This means you can stick an if block there with a write-line to give you your groups of four.
List<int> endResult = new List<int>();
StringBuilder tempSb = new StringBuilder();
for(int i=0; i < groups.Count; i++)
{
if(i % 4 == 0) {
endResult.Add(int.Parse(sb.ToString()));
tempSb.Clear(); // remove what was already added
}
tempSb.Append(group[i]);
}
// check to make sure there aren't any stragglers left in
// the StringBuilder. Would happen if the count of groups is not a multiple of 4
if(groups.Count % 4 != 0) {
groups.Add(int.Parse(sb.ToString()));
}
This will give you a list of 4 digit ints and make sure you don't lose any if your the number of ints in your groups list is not a multiple of 4. Please note that I am continuing based on what you provided, so groups is the ArrayList of ints.
This is some thing I quickly put together:
Update:
ArrayList guesses = new ArrayList(); //This is the ArrayList
// Four or more
guesses.Add(1); guesses.Add(2);
guesses.Add(3);guesses.Add(4);
guesses.Add(5); guesses.Add(6); guesses.Add(7);guesses.Add(8); guesses.Add(9);
//Uncomment-Me for less than four inputs
//guesses.Add(1); guesses.Add(2);
int position = 0;
if (guesses.Count < 4)
{
for (int y = 0; y < guesses.Count; y++)
{
Console.Out.Write(guesses[y]);
}
}
else
{
for (int i = 1; i <= guesses.Count; i++)
{
if (i%4 == 0)
{
Console.Out.WriteLine(string.Format("{0}{1}{2}{3}", guesses[i - 4], guesses[i - 3],
guesses[i - 2], guesses[i - 1]));
position = i;
}
else
{
if (i == guesses.Count)
{
for (int j = position; j < i; j++)
{
Console.Out.Write(guesses[j]);
}
}
}
}
}

Categories