Reputation: 3
I'm trying to create a leaderboard system by using a merge sort algorithm that sorts the scores in ascending order and then sorts the usernames by their score. I then use a for loop that displays the scores in descending order. The code in the image works perfectly fine when each user has a unique score and duplicate scores do not exist. However, I am having a problem when more than one user has the same score (i.e. there are duplicate scores).
For example, if two users have the same score (in this case 5), the wrong username is displayed. I believe this is because it only looks at the first occurrence of the sorted score. Hence, it's overwriting the original name. Is there any way I can skip the first occurrence of the duplicate number after it has been visited?
string[] UsernameAndScoresArray = System.IO.File.ReadAllLines(@"UsernamesAndScores.txt");
string[] UnsortedUsernamesArray = new string[UsernameAndScoresArray.Length];
int[] UnsortedScoresArray = new int[UsernameAndScoresArray.Length];
string UsernameAndScore = "";
string Username = "";
int Score = 0;
int position = 0;
for (int i = 0; i < UsernameAndScoresArray.Length; i++)
{
//Locates the username and scores and stores them in an 'unsorted array'
UsernameAndScore = UsernameAndScoresArray[i];
position = UsernameAndScore.IndexOf(':');
Username = UsernameAndScore.Substring(0, position);
UnsortedUsernamesArray[i] = Username;
position = UsernameAndScore.IndexOf(':');
Score = int.Parse(UsernameAndScore.Remove(0, position + 1));
UnsortedScoresArray[i] = Score;
}
//Sorts the Scores in ascending order using the merge sort algorithm
SortedArray = MergeSort(UnsortedScoresArray);
SortedUsernames = new string[SortedArray.Length];
for (int i = 0; i < UnsortedScoresArray.Length; i++)
{
for (int a = 0; a < SortedArray.Length; a++)
{
if (UnsortedScoresArray[i] == SortedArray[a])
{
//The usernames are sorted based on the scores
SortedUsernames[a] = UnsortedUsernamesArray[i];
}
}
}
int place = 0;
for (int i = SortedArray.Length - 1; i >= 0; i--)
{
//The place, username and number of points are displayed in descending order
place++;
Username = SortedUsernames[i];
Score = SortedArray[i];
ListBoxLeaderBoardPlaceAndUser.Items.Add(place + ": " + Username);
ListBoxLeaderboardScore.Items.Add(Score);
}
//This is an example of the error
UnsortedScoresArray[] = {46, 7, 5, 10, 5}
UnsortedUsernamesArray[] = {User1, User2, User3, User4, User5}
SortedScoresArray[] = {5, 5, 7, 10, 46}
SortedUsernamesArray[] = {User5, User5, User2, User4, User1}
It should say SortedUsernamesArray[] = {User3, User5, User2, User4, User1}
Upvotes: 0
Views: 101
Reputation: 172438
How do you skip a number which you've already visited in an array?
By remembering the position, for example, in a HashSet
. Here's how I'd adapt your code to do that:
var usedAlready = new HashSet<int>();
for (int a = 0; a < SortedArray.Length; a++)
{
for (int i = 0; i < UnsortedScoresArray.Length; i++)
{
if (UnsortedScoresArray[i] == SortedArray[a] && !usedAlready.Contains(i))
{
SortedUsernames[a] = UnsortedUsernamesArray[i];
usedAlready.Add(i);
// We already found the user for position a, no need to continue the inner loop.
break;
}
}
}
Note that I've switched your loops around (first a
(through SortedArray), then i
).
Upvotes: 0
Reputation: 172438
The idiomatic way to solve such an issue in C# would be to
store both user name and score in the same data structure
public class UserAndScore
{
public string User { get; }
public int Score { get; }
public UserAndScore(string user, int score)
{
User = user;
Score = score;
}
}
and then use the built-in sort functionality of .NET to do the work for you:
UserAndScore[] unsorted = new[]
{
new UserAndScore("User1", 46),
new UserAndScore("User2", 7),
// ...
};
// No need to re-invent the wheel... let .NET do the sorting!
UserAndScore[] sorted = unsorted.OrderBy(u => u.Score).ToArray();
Upvotes: 0
Reputation: 45
For this kind of problem, I would use a dictionary. I know it is a bother to get an answer that suggests a completely different data structure but eventually, you will get benefitted from its use.
I like this approach:
var dict = Enumerable.Range(0, keys.Length).ToDictionary(i => keys[i], i => values[I]);
Upvotes: 0
Reputation: 3495
You can also sort user by score
:
int[] UnsortedScoresArray = { 46, 7, 5, 10, 5 };
string[] UnsortedUsernamesArray = { "User1", "User2", "User3", "User4", "User5" };
int[] sortedScoresArray=new int[UnsortedScoresArray.Length];
string[] sortedUsernamesArray = new string[UnsortedUsernamesArray.Length];
for (int i = 0; i < UnsortedScoresArray.Length; i++)
{
for (int j = i; j < UnsortedScoresArray.Length; j++)
{
if (UnsortedScoresArray[i] > UnsortedScoresArray[j])
{
int temp;
temp = UnsortedScoresArray[i];
UnsortedScoresArray[i] = UnsortedScoresArray[j];
UnsortedScoresArray[j] = temp;
string temp2 = UnsortedUsernamesArray[i];
temp2 = UnsortedUsernamesArray[i];
UnsortedUsernamesArray[i] = UnsortedUsernamesArray[j];
UnsortedUsernamesArray[j] = temp2;
}
}
}
sortedScoresArray = UnsortedScoresArray;
sortedUsernamesArray = UnsortedUsernamesArray;
Upvotes: 0