1

I'm having multiple problems with my program (C#.NET) and have no idea what's causing them.

The program is intended to sort a list of names and birthdays (formatted first name,last name,DD/MM/YYYY) in ascending and descending order by first name, last name, and birthday. It is also to have other functions which have not yet been implemented.

The first problem is in the quikSortStr method. The program crashes in the first if block, stating that j is out of the bounds of the array. This happens whether or not mode == "asc".

The second, and more confusing problem is that when the values are loaded from a text file, every odd-indexed value of first and last will be null, while each odd-indexed value of bDay will be 1/1/0001.

I've included the full program below for reference, a quicksort method and the use of parallel arrays are required. My apologies for the lack of comments.

Thanks in advance for any help. I'm completely stumped.

namespace Names_Arrays
{
public partial class frmNamArrays : Form
{
    System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo("en-CA");
    string[] first;
    string[] last;
    DateTime[] bDay;
    string order = "asc";
    string format = "d/M/yyyy";

    public frmNamArrays()
    {
        InitializeComponent();
    }
    private void write()
    {
        string[] lines = new string[first.Length];

        for (int i = 0; i < lines.Length; i++)
            lines[i] = first[i] + ',' + last[i] + ',' + bDay[i].ToString(format);

        txtbxNames.Clear();
        txtbxNames.Lines = lines;
    }

    private void load()
    {
        string[] lines = txtbxNames.Lines;

        first = new string[lines.Length];
        last = new string[lines.Length];
        bDay = new DateTime[lines.Length];

        int i = 0;
        foreach (string line in lines)
        {
            string[] data = line.Split(',');

            //There aren't any lines that split to a string[] of length less than three,
            //but for some reason the program kept believing there are.
            //patched that leak.

            if (data.Length == 3)
            {
                first[i] = data[0];
                last[i] = data[1];
                bDay[i] = Convert.ToDateTime(data[2], culture);
            }
            i++;
        }
    }
    public DateTime[] quikSortTim(DateTime[] primary, string mode, int left, int right)
    {
        if (primary.Length > 1)
        {
            int i = left, j = right;
            DateTime pivot = primary[left + (right - left) / 2];

            while (i <= j)
            {
                if (mode == "asc")
                {
                    while (DateTime.Compare(primary[i], pivot) < 0)
                        i++;
                    while (DateTime.Compare(primary[j], pivot) > 0)
                        j--;
                }
                else
                {
                    while (DateTime.Compare(primary[i], pivot) > 0)
                        i++;
                    while (DateTime.Compare(primary[j], pivot) < 0)
                        j--;
                }
                if (i <= j)
                {
                    DateTime holdoverB = primary[i];
                    primary[i++] = primary[j];
                    primary[j--] = holdoverB;

                    string holdover = last[i - 1];
                    last[i] = last[j + 1];
                    last[j] = holdover;

                    holdover = first[i - 1];
                    first[i] = first[j + 1];
                    first[j] = holdover;

                }
            }
            if (j > left)
                primary = quikSortTim(primary, mode, left, j);
            if (i < right)
                primary = quikSortTim(primary, mode, i, right);
        }
        return primary;
    }

    public string[] quikSortStr(string[] primary, string type, string mode, int left, int right)
    {
        if (primary.Length > 1)
        {
            int i = left, j = right;
            string pivot = primary[left + (right - left) / 2];

            while (i <= j)
            {
                if (mode == "asc")
                {
                    while (String.Compare(primary[i], pivot) < 0)
                        i++;
                    while (String.Compare(primary[j], pivot) > 0)
                        j--;
                }
                else
                {
                    while (String.Compare(primary[i], pivot) > 0)
                        i++;
                    while (String.Compare(primary[j], pivot) < 0)
                        j--;
                }
                if (i <= j)
                {
                    string holdover = primary[i];
                    primary[i] = primary[j];
                    primary[j] = holdover;
                    if (type == "first")
                    {
                        holdover = last[i];
                        last[i] = last[j];
                        last[j] = holdover;
                    }
                    else
                    {
                        holdover = first[i];
                        first[i] = first[j];
                        first[j] = holdover;
                    }
                    DateTime holdoverBeta = bDay[i];
                    bDay[i] = bDay[j];
                    bDay[j] = holdoverBeta;
                    i++;
                    j++;
                }
            }
            if (j > left)
                primary = quikSortStr(primary, type, mode, left, j);
            if (i < right)
                primary = quikSortStr(primary, type, mode, i, right);
        }
        return primary;
    }

    private void frmNamArrays_SizeChanged(object sender, EventArgs e)
    {
        txtbxNames.Width = this.Width - 40;
        txtbxNames.Height = this.Height - 157;
    }

    private void btnSort_Click(object sender, EventArgs e)
    {
        load();

        switch (cbobxCategory.Text)
        {
            case ("First Name"):
                first = quikSortStr(first, "first", order, 0, first.Length - 1);
                break;
            case ("Last Name"):
                last = quikSortStr(last, "last", order, 0, last.Length - 1);
                break;
            case ("Birthday"):
                bDay = quikSortTim(bDay, order, 0, bDay.Length - 1);
                break;
            default:
                break;
        }

        write();
    }

    private void cbobxOrder_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (cbobxOrder.Text == "Ascending")
            order = "asc";
        else
            order = "desc";
    }

    private void displayfile(string name)
    {
        StreamReader fileData = new StreamReader(name);

        txtbxNames.Lines = fileData.ReadToEnd().Split('\n');

    }

    private void mnuOpen_Click(object sender, EventArgs e)
    {
        OpenFileDialog open = new OpenFileDialog();
        open.Filter = "Text Files|*.txt";
        open.Title = "Select a text file...";

        if (open.ShowDialog() == DialogResult.OK && open.FileName != "")
            displayfile(open.FileName);
    }

    private void mnuExit_Click(object sender, EventArgs e)
    {
        this.Close();
    }
}
}
user1576628
  • 792
  • 3
  • 8
  • 25
  • Have you considered writing a unit test? Separate your sorting logic into its own class and then write a test with sample data. Have you considered using a Person class? – What Would Be Cool May 05 '13 at 04:09

2 Answers2

1

You have to change the code as below in the quikSortStr method inside the if (i <= j) loop

    DateTime holdoverBeta = bDay[i];
    bDay[i] = bDay[j];
    bDay[j] = holdoverBeta;
    i++;
    j--;//was: j++;

and this will fix the issue.

Saravanan
  • 7,637
  • 5
  • 41
  • 72
0

Thanks to saravanan for pointing out my first mistake. The out of range error was caused by an accidental incrementation of j in the wrong direction. The fixed methods are

    public DateTime[] quikSortTim(DateTime[] primary, string mode, int left, int right)
    {
        if (primary.Length > 1)
        {
            int i = left, j = right;
            DateTime pivot = primary[left + (right - left) / 2];

            while (i <= j)
            {
                if (mode == "asc")
                {
                    while (DateTime.Compare(primary[i], pivot) < 0)
                        i++;
                    while (DateTime.Compare(primary[j], pivot) > 0)
                        j--;
                }
                else
                {
                    while (DateTime.Compare(primary[i], pivot) > 0)
                        i++;
                    while (DateTime.Compare(primary[j], pivot) < 0)
                        j--;
                }
                if (i <= j)
                {
                    DateTime holdoverB = primary[i];
                    primary[i] = primary[j];
                    primary[j] = holdoverB;

                    string holdover = last[i];
                    last[i] = last[j];
                    last[j] = holdover;

                    holdover = first[i];
                    first[i] = first[j];
                    first[j] = holdover;

                    i++;
                    j--;
                }
            }
            if (j > left)
                primary = quikSortTim(primary, mode, left, j);
            if (i < right)
                primary = quikSortTim(primary, mode, i, right);
        }
        return primary;
    }

    public string[] quikSortStr(string[] primary, string type, string mode, int left, int right)
    {
        if (primary.Length > 1)
        {
            int i = left, j = right;
            string pivot = primary[left + (right - left) / 2];

            while (i <= j)
            {
                if (mode == "asc")
                {
                    while (String.Compare(primary[i], pivot) < 0)
                        i++;
                    while (String.Compare(primary[j], pivot) > 0)
                        j--;
                }
                else
                {
                    while (String.Compare(primary[i], pivot) > 0)
                        i++;
                    while (String.Compare(primary[j], pivot) < 0)
                        j--;
                }
                if (i <= j)
                {
                    string holdover = primary[i];
                    primary[i] = primary[j];
                    primary[j] = holdover;
                    if (type == "first")
                    {
                        holdover = last[i];
                        last[i] = last[j];
                        last[j] = holdover;
                    }
                    else
                    {
                        holdover = first[i];
                        first[i] = first[j];
                        first[j] = holdover;
                    }
                    DateTime holdoverBeta = bDay[i];
                    bDay[i] = bDay[j];
                    bDay[j] = holdoverBeta;
                    i++;
                    j--;
                }
            }
            if (j > left)
                primary = quikSortStr(primary, type, mode, left, j);
            if (i < right)
                primary = quikSortStr(primary, type, mode, i, right);
        }
        return primary;
    }

I did not find a solution for the second issue per se. I did, however, discover that all of the elements that read ",,1/1/0001" were added, and did not replace any names. Using this, I added to the array lines only the index values that did not contain "1/1/0001". I then used lines = lines.Where(s => s != null).ToArray(); to shorten lines by eliminating all of the now null-type values. The modified function is below.

private void write()
    {
        string[] lines = new string[first.Length];

        for (int i = 0; i < lines.Length; i++)
            if (bDay[i].ToString(format) != "1/1/0001")
                lines[i] = first[i] + ',' + last[i] + ',' + bDay[i].ToString(format);

        lines = lines.Where(s => s != null).ToArray();

        txtbxNames.Clear();
        txtbxNames.Lines = lines;
    }

Thanks for the help. I found the resource for my solution here.

EDIT: It seems the problem is inherent in StreamReader.ReadToEnd(). I'm not sure why, but it can be completely avoided by using System.IO.File.ReadAllLines(filepath). In the original code, I would replace

StreamReader file = new StreamReader(name);
lines = file.ReadToEnd().Split('\n');

with

lines = File.ReadAllLines(name);

and add using System.IO;.

Community
  • 1
  • 1
user1576628
  • 792
  • 3
  • 8
  • 25
  • For your kind information, by default the Sort API internally uses QuickSort in .Net. Thanks for posting the solution for others to use as a reference. – Saravanan May 06 '13 at 04:48
  • It's not so much a solution as a workaround, but thanks. Unfortunately for me, I am also required to write my sorting algorithms from scratch, but I'll keep that in mind for when I don't have these conditions on me. – user1576628 May 06 '13 at 22:55