Dubellest
Dubellest

Reputation: 82

How can I reorganize a list of strings based on a number inside of said string?

I am trying to create a scoreboard for my geography quiz app that I'm making. I am trying to organize the scores by the largest to the smallest and I have no idea where to even start. Here is the code for the submit button function:

    private void button1_Click(object sender, EventArgs e)//Submit Button
    {
        if(isDone)
        {
            string[] lines = System.IO.File.ReadAllLines(path+"/score.txt");
            StreamWriter sw = new StreamWriter(path+"/score.txt");
            foreach (string line in lines)
            {
                if (line != null && line.Length > 0) {sw.WriteLine("\n"+ line); }
            }
            sw.WriteLine("Score:" + score +" ~"+ textBox1.Text + " -- " + label9.Text + " -- " + numCorrect + "/41" );
            sw.Close();
        }
    }

I would like to sort it by the score variable and a number that is grabbed from the line in the text file

Upvotes: 0

Views: 43

Answers (1)

Rufus L
Rufus L

Reputation: 37020

I'm making a few assumptions since you haven't updated your question, but assuming that the file line contains lines in the format: "Score: [score] ~[Name] -- Timer: [mm:ss] -- [numCorrect]/41", and that score is a double and numCorrect is an int (you don't show where they come from), then here's one way to handle this situation.

First, create a class with the properties you want to store that has the ability to create an instance of itself from a string (file line) and output itself as a string (for writing to a file):

private class Result
{
    public string Name { get; set; }
    public double Score { get; set; }
    public TimeSpan Time { get; set; }
    public int CorrectCount { get; set; }

    /// <summary>
    /// Returns an instance of the Result class based on a string.
    /// The string must be in the format:
    /// "Score: [score] ~[Name] -- Timer: [mm:ss] -- [numCorrect]/41"
    /// Where [score] is a valid double and [numCorrect] a valid int
    /// </summary>
    /// <param name="input">The string to parse</param>
    /// <returns>A Result with properties set from the input string</returns>
    public static Result Parse(string input)
    {
        if (input == null) throw new ArgumentNullException(nameof(input));

        var splitStrings = new[] {"Score:", " ~", " -- ", "/41"};

        var parts = input
            .Split(splitStrings, StringSplitOptions.RemoveEmptyEntries)
            .Select(item => item.Trim())
            .ToList();

        // These will hold the converted parts of the string
        double score;
        int correctCount;
        TimeSpan time;

        // Verify that the string contains 4 parts, and that the Score, Time, and
        // CorrectCount parts can be converted to the proper data type for the property
        if (parts.Count != 4 ||
            !double.TryParse(parts[0], out score) ||
            !TimeSpan.TryParseExact(parts[2], @"mm\:ss", 
                CultureInfo.InvariantCulture, out time) ||
            !int.TryParse(parts[3], out correctCount))
        {
            throw new FormatException("input is not in a recognized format");
        }

        return new Result
        {
            Name = parts[1],
            Score = score,
            Time = time,
            CorrectCount = correctCount
        };
    }

    public override string ToString()
    {
        return $"Score:{Score} ~{Name} -- {Time.ToString(@"mm\:ss")} -- {CorrectCount}/41";
    }
}

Then create a method that can create an instance of this class from your form data:

// Not sure where these come from so created these class fields
private const string Path = @"f:\public\temp\score.txt";
private double score = 0;
private int numCorrect = 0;

private static Result GetResultFromFormData()
{
    return new Result
    {
        Score = score,
        Name = textBox1.Text,
        Time = TimeSpan.ParseExact(label9.Text, @"mm\:ss", CultureInfo.InvariantCulture),
        CorrectCount = numCorrect)
    };
}

Now we can populate a list of these classes from both the file contents and the form. Then we can sort the list using Linq on any field we want to (Score in this case), and can write the sorted list back to the file:

private void button1_Click(object sender, EventArgs e)//Submit Button
{
    if (isDone)
    {
        // Create a list of results from our file
        List<Result> existingResults = File.ReadAllLines(Path).Select(Result.Parse).ToList();

        // Add a new result to the list from the form data
        existingResults.Add(GetResultFromFormData());

        // Sort the list on the Score property
        existingResults = existingResults.OrderBy(result => result.Score).ToList();

        // Write the sorted list back to the file
        File.WriteAllLines(Path, existingResults.Select(result => result.ToString()));
    }
}

Now the file contains it's original contents, plus the new result from the form, all sorted by Score.

Upvotes: 1

Related Questions