0

I have a button which should take a 'surname' string input, search a directory array for the 'record' structure correlating to that surname, and output that record to a listview. The directory consists of many rows of 3 string record structures: surname, forename, extcode.

After days of scouring similar SO questions and other sources, I've pieced together the methods which seem to fit my problem best, but my primary search method is not, for some reason, referencing my secondary method. Can you tell me why you think this is, whether you think it will work if I manage to get the reference working, and if not, any alternative suggestions?

FYI my my if statement is a little unruly as my project requires that the surname be case insensitive and accept partial string matches.

private void SearchSurname()
    {
        Array.Sort(directory, (x, y) => String.Compare(x.surname, y.surname));

        ClearForm();
        int surnameIndex = Array.BinarySearch(directory, txtSurname.Text);

        if (directory[surnameIndex].surname.ToUpper().Substring(0, txtSurname.Text.Length).Contains(txtSurname.Text.ToUpper()))
        {
            ListViewItem record = new ListViewItem();

            // Send each field in current record to single listview item
            record.Text = (Convert.ToString(txtSurname.Text));
            record.SubItems.Add(Convert.ToString(txtForename.Text));
            record.SubItems.Add(Convert.ToString(txtExtCode.Text));

            // Display new record listview item in listview
            lvDirectory.Items.Add(record);
        }
    }

    public int BinarySearch(string[] directory, string searchTerm)
    {
        int first = 0;
        int last = directory.Length - 1;
        int position = -1;
        bool found = false;
        int compCount = 0;
        searchTerm = txtSurname.Text;

        while (found != true && first <= last)
        {
            int middle = (first + last) / 2;

            if (string.Compare(directory[middle], searchTerm, true) == 0)
            {
                found = true;
                position = middle;
                compCount++;
            }
            else if (string.Compare(directory[middle], searchTerm, true) > 0)
            {
                last = middle;
                compCount++;
            }
            else
            {
                first = middle;
                compCount++;
            }
        }
        return position;
    }

Edit: Updated code per Olivier Jacot-Descombes' answer:

private void SearchSurname()
    {
        // Sort directory alphabetically by surname
        Array.Sort(directory, (x, y) => String.Compare(x.surname, y.surname));

        ClearForm();

        // In directory, find line index of search term
        int surnameIndex = BinarySearch(directory, txtSurname.Text);

        ListViewItem record = new ListViewItem();

        // Send each field in current record to single listview item
        record.Text = (Convert.ToString(directory[surnameIndex].surname));
        record.SubItems.Add(Convert.ToString(directory[surnameIndex].forename));
        record.SubItems.Add(Convert.ToString(directory[surnameIndex].extCode));

        // Display new record listview item in listview
        lvDirectory.Items.Add(record);
    }

    private int BinarySearch(record[] directory, string searchTerm)
    {
        int first = 0;
        int last = directory.Length - 1;
        int surnameIndex = -1;
        bool indexFound = false;

        // While index not found and there are still points in the array to check
        while (indexFound != true && first < last)
        {
            int middle = (first + last) / 2;

            // If surname field in middle record of directory array matches the search term
            if (string.Compare(directory[middle].surname, searchTerm, true) == 0)
            {
                // Index found!
                indexFound = true;
                surnameIndex = middle;
                MessageBox.Show("If 1");
            }
            // If surname field in middle record of directory array is higher, alphabetically, than the search term
            else if(string.Compare(directory[middle].surname, searchTerm, true) > 0)
            {
                // The next search will be between the first and the current middle records of the array
                last = middle;
                MessageBox.Show("If 2");
            }
            // If surname field in middle record of directory array is lower, alphabetically, than the search term
            else
            {
                // The next search will be between the current middle and the highest records of the array
                first = middle;
                MessageBox.Show("If 3");
            }
        }
        return surnameIndex;
    }

1 Answer 1

1

Your SearchSurname method calls Array.BinarySearch, which is a static method of the Array Class. If you wanted to call your own method, you would have to write:

int surnameIndex = BinarySearch(directory, txtSurname.Text); // Without "Array."

You are using case-insensitive comparison through the 3rd parameter of String.Compare being true.

You could use

if (string.StartsWith(directory[middle], searchTerm,
                      StringComparison.CurrentCultureIgnoreCase))

To search only for beginning of names. But there is a problem if several names match. E.g. You are searching for "smit" and you have "Smith" and "Smithy" in the array. Therefore, you would have to test the entries before and after the found match, and return all matching ones as possible results.

Sign up to request clarification or add additional context in comments.

11 Comments

Thanks for clarifying-- I couldn't decide whether to use this or not. I've removed this per your suggestion but, on that reference to the directory, I now recieve the error "Cannot convert from 'PhoneDirectory.frmPhoneDirectory.record[]' to 'string[]". Would you be able to tell me how I can resolve this?
Your array seems to be of type record[], not string[]. Therefore, change your method: public int BinarySearch(record[] directory, string searchTerm)
When I change my method to, public int BinarySearch(record[] directory, string searchTerm), I receive the error: "Inconsistent accessibility: parameter type 'frmPhoneDirectory.record[]' is less accessible than method 'frmPhoneDirectory.BinarySearch(frmPhoneDirectory.record[], string)"
Your method is public but record is not. Make record public as well.
Ah, of course. Making them both private has fixed this (the rest of the methods in my program are private). There are now no errors, however when I run and search, the program appears to crash with no clue as to why in the debugger. Presumably this is because it's searching infinitely? I've added the latest bug-free code in the original post. Thanks so much for your help so far.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.