Quick Search out of bounds of array and confusing array behaviour

143 Views Asked by At

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();
    }
}
}
2

There are 2 best solutions below

2
On BEST ANSWER

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;.

1
On

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.