0

I'm getting an index out of bounds error when I request the last element in the list for a C# Windows Form Application.

Does anyone know what might be causing this because it literally makes no sense whatsoever. It's as if the computer is miscalculating. The size is supposedly 17, but everything above index 5 gives an error. Once logic goes out the window, I've got nothing left. Perhaps there's something weird going on behind the scenes that someone else may have encountered before?

List<string> words = new List<string>();

        List<string> words = new List<string>();
        string word = "";
        int idx = 0;
        while (idx < notes.Length)
        {
            word += notes[idx];
            if (notes[idx] == ' ')
            {
                words.Add(word);
                word = "";
            }
            ++idx;
        }
        string notes1 = "";
        string notes2 = "";
        string notes3 = "";

        int one_third = words.Count / 3;
        int two_thirds = (words.Count / 3) * 2;

        int k;
        for (k = 0; k < one_third; k++)
            notes1 += words.ElementAt(k) + ' ';
        for (k = one_third; k < two_thirds; k++)
            notes2 += words[k] + ' ';
        for (k = two_thirds; k < words.Count; k++)
            notes3 += words[k] + ' ';

        notesLabel1.Text = notes1;
        notesLabel2.Text = notes2;
        notesLabel3.Text = notes3;

++++++++++++++++++++++++++++++++++++++++++++++

FOUND THE PROBLEM!!!!!!

Basically, I overworked myself yesterday so my brain was fried by the end of the day and I was irritable. The function code works just fine unless, like many have said, the notes string is empty. I knew that the notes string wasn't empty because it posts just fine without the +1 portion in the for-loop. BUT I FORGOT ABOUT ONE THING. The first 'item' that gets posted to the form is the first 'item' in the array I have in my program. Even though the item notes in question do indeed have 17 words, it's the second item in the list. The first item in the list gets posted when the application loads and I simply scroll over to the item with the 17 word notes. The first item that's posted doesn't have notes so the first time that function is called, the argument is an empty string. OOPS! feels dumb

Thanks, everyone!! I appreciate you taking the time to help fix my nonsense here. haha

Keith Salmon
  • 89
  • 3
  • 9
  • Add a debug statement printing both `list.Count` and the index you want to access. Probably one of them is not what you expect. In your example the index size is `0` and the index `-1` which is out of bounds. – CodesInChaos May 10 '13 at 22:53
  • 1
    Did you check the value of `words.Count`? Can you show the code you use to fill the list? – RST May 10 '13 at 22:53
  • 1
    Possibly you wrote broken multi-threaded code that corrupted the internal state of your list. – CodesInChaos May 10 '13 at 22:53
  • My thoughts initially @CodesInChaos. – Edper May 10 '13 at 23:01
  • Can you show the error? – anouar.bagari May 10 '13 at 23:09
  • Nope, no multithreading. Yes, I know that I didn't add any elements in my example, but I did in my program. Check out this nonsense right here.... This compiles: if (words.Count > 1) notes1 += words.ElementAt(5); --- This does not compile: notes1 += words.ElementAt(5); --- So if I don't make some arbitrary check then I get an error. Complete garbage. – Keith Salmon May 10 '13 at 23:10
  • Here's a link to the error. Don't spend too much time on it. I'll probably just do it another way. It just irritates the hell out of me when my code is right, but I still get errors. Ugh.. http://oi44.tinypic.com/4ugfwh.jpg – Keith Salmon May 10 '13 at 23:13
  • just a hint : when i see strange behaviour, i do a 'clean' + i re-launch VS. What is the vakue of k for the bug ? what is the value of words.Count given by the debugger ? – GameAlchemist May 10 '13 at 23:14
  • @KeithSalmon: It is a very rare day that a programmers code is right and they are receiving an error. I put together a very simple test using your code. The ONLY thing I added was putting 10 words in the `words` list. Worked perfectly. This tells me that `words` doesn't contain what you think it does. – NotMe May 10 '13 at 23:21
  • I know it's rare, but that's what ALWAYS seems to happen to me. I had to come in and see the instructors at my university three times due to "bad code" that ended up being some sort of computer glitch so now I don't trust anything!! lol. I'll try adding new words and see what happens.. – Keith Salmon May 10 '13 at 23:25
  • I assume `notes` is a string? What are its contents when you run the tesT? – Matthew Watson May 10 '13 at 23:27
  • Why should words.ElementAt(5) throw an error, but if (words.Count > 1) words.ElementAt(5) works just fine? That's my main issue. – Keith Salmon May 10 '13 at 23:27
  • @KeithSalmon Where does your notes variable comes from? Could you show that also? – Edper May 10 '13 at 23:27
  • The contents are all there. They show up just fine like they should if I do an if-statement before the call. But the if-statement doesn't even check anything pertinent. – Keith Salmon May 10 '13 at 23:28
  • Can I just say that's an incredibly convoluted way of splitting notes into an array of words? you could just use `notes.split(' ')`. Also note that your "thirds" might be floats not integers... – Basic May 10 '13 at 23:30
  • Oh yeah my method of splitting them is absurd, but I was in a hurry. lol. Thanks for the advice!! The thirds should be integers since I didn't make any sort of float cast. – Keith Salmon May 10 '13 at 23:31
  • @KeithSalmon Can you post a compilable program that demonstrates the problem? You could determine the string that causes the error and hard-code it into the test program so other people can compile it. (It's not currently compilable of course). If you just added `string Notes = "whatever the string is";` to the beginning that would do it. – Matthew Watson May 10 '13 at 23:31
  • Yeah I'll see about putting something together here later this evening. I really appreciate everyone's insight into this issue!!!! – Keith Salmon May 10 '13 at 23:33
  • 1
    @KeithSalmon - Never concatenate strings in loops! You should be using a `StringBuilder`. See: [String vs StringBuilder](http://stackoverflow.com/questions/73883/string-vs-stringbuilder) and the [MSDN documentation](http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx) – Matt Johnson-Pint May 10 '13 at 23:58
  • Show us the content of `notes`. That part is important because, without it, your `words` list will have zero entries and Matthew Watson's answer applies. – Simon Whitehead May 11 '13 at 00:36
  • Blaming a bug on the framework instead of your own code is being an ignorant programmer. Millions of people use the .NET Framework, I highly doubt you'd be the first to encounter this issue. – Dave Zych May 11 '13 at 00:37
  • word.Count/3+1 isn't guaranteed to be at most word.Count. Consider the empty list: you're going to try to access element 0, yet there is no such element. – redtuna May 11 '13 at 01:36
  • Thanks Matt Johnson! I need to learn more about the best way to utilize .NET classes. I'll try that out. – Keith Salmon May 11 '13 at 02:12

5 Answers5

3

The errors are fairly straightforward, and we can explain all the symptoms.

Firstly note that:

List<string> words = new List<string>();

gives you an EMPTY array for which words.Count == 0. In particular, note that words.ElementAt(0) will throw.

Ok so with this line (which throws):

string the_word = words.elementAt(words.Count - 1);

Clearly (words.Count - 1) is -1 and thus the error.

The following code DOES NOT throw an error:

for (k = 0; k < words.Count / 3; k++) notes1 += words.ElementAt(k) + ' ';

because the for loop runs while k < 0, and so the loop doesn't even execute a single iteration.

Now this one:

for (k = 0; k < words.Count / 3 + 1; k++) notes1 += words.ElementAt(k) + ' ';

This loop will run while k < 1 so it will execute once with k=0, and thus ElementAt(0) will throw because the array is empty.

Finally:

for (k = 0; k < words.Count; k++) notes1 += words.ElementAt(k);

Again this will not iterate even once. words.Count is 0, so it runs while k < 0 which means it doesn't iterate.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
1

if words is empty, you are getting words.Count - 1 means -1, how can you do:

words.elementAt(-1);
diwatu
  • 5,641
  • 5
  • 38
  • 61
  • It's not empty though. The count is 17. – Keith Salmon May 10 '13 at 22:53
  • for (k = 0; k < words.Count / 3 + 1; k++) notes1 += words.ElementAt(k) + ' '; THROWS ERROR for (k = 0; k < words.Count / 3; k++) notes1 += words.ElementAt(k) + ' '; NO ERROR ??? MAKES NO SENSE ??? – Keith Salmon May 10 '13 at 22:55
  • Are you sure you have elements when you are testing? Only when it is empty, your error can happen. because if size is 0,then:for (k = 0; k < words.Count / 3 + 1; k++) means for (k = 0; k < 1; k++), still try to get words.ElementAt(0), but nothing is in words. – diwatu May 10 '13 at 23:03
  • Look at my latest test. It works if I check an arbitrary value, but only then. Logic is non-existent with this issue. – Keith Salmon May 10 '13 at 23:15
  • 1
    show us the definition of k – diwatu May 10 '13 at 23:18
  • The problem could be in the content of Words. something like if words[x] is null, it will cause a exception. You should debug this and find where it is wrong. – diwatu May 10 '13 at 23:34
1

Notice something in your code down below:

if (notes[idx] == ' ')
        {
            words.Add(word);
            word = "";
        }

You add to your words List when the notes is empty? like notes[idx] == ' '

Could it be testing if it is not empty before you add it, like?

if (!String.IsNullOrEmpty(notes[idx]))
        {
            words.Add(word);
            word = "";
        }

Just guessing.

Original Post:

Check the count first before doing elementAt, so instead of:

 string the_word = words.elementAt(words.Count - 1);

Do like :

 string the_word = words.Count>0 ? words.elementAt(words.Count - 1) : "";

And probably it would be good if you check it with messagebox just for debugging if the count is really 17 as you claim like:

 MessageBox.Show(words.Count);
 string the_word = words.Count>0 ? words.elementAt(words.Count - 1) : "";

Update:

I did a sampling in my own PC with the code below that came from your posting and it works with no error. Could it be that you're using multi-threading and it tries to change the value of your words List?

 List<string> words = new List<string>();
        String notes1="";

        int k;
        words.Add("The quick brown fox jumps over the lazy dog.");
        string the_word = words.ElementAt(words.Count - 1);
        MessageBox.Show(the_word);

        for (k = 0; k < words.Count / 3; k++)
        { notes1 += words.ElementAt(k) + ' '; }


        for (k = 0; k < words.Count / 3 + 1; k++)
        { notes1 += words.ElementAt(k) + ' '; }
Edper
  • 9,144
  • 1
  • 27
  • 46
  • This is nonsense. Take a look at this... THIS COMPILES: if (words.Count > 1) notes1 += words.ElementAt(5); THIS DOES NOT COMPILE: notes1 += words.ElementAt(5); So I'm getting an error if I don't make some arbitrary check. Something is wrong within the .NET environment.... – Keith Salmon May 10 '13 at 23:08
  • @KeithSalmon I hope you see my new observation in your code. – Edper May 10 '13 at 23:54
  • I did. Thank you. I appreciate you going out of your way to help me track down this error. It ended up being a silly mishap on my part. I simply forgot that the function was first being called with an empty string as the argument. – Keith Salmon May 11 '13 at 16:59
1

If you're code only contains a single space, then it will only have one word in the array.

The result of one_third and two_thirds will then always be zero IF words.Count is less than 3. Which means the rest of your code will never find them.

When you add +1 to the words.count, then it will go outside the bounds of the array.

All in all this is a really round about (and error filled) way of splitting the notes.

UPDATE

I used the following test code:

        Char[] notes = "there are seventeen words in this list make the most of them because it really counts here".ToCharArray();

        List<string> words = new List<string>();
        string word = "";
        int idx = 0;
        while ( idx < notes.Length ) {
            word += notes[idx];
            if ( notes[idx] == ' ' ) {
                words.Add(word);
                word = "";
            }
            ++idx;
        }
        string notes1 = "";
        string notes2 = "";
        string notes3 = "";

        int one_third = words.Count / 3;
        int two_thirds = ( words.Count / 3 ) * 2;
        int k;

        for ( k = 0; k < one_third +1; k++ )
            notes1 += words.ElementAt(k) + ' ';

        for ( k = one_third; k < two_thirds; k++ )
            notes2 += words[k] + ' ';

        for ( k = two_thirds; k < words.Count; k++ )
            notes3 += words[k] + ' ';

        MessageBox.Show(notes1);

and had no errors. The result was not exactly what you normally expect, but there were no errors. Note that I put in the +1 that was the stated problem.


An alternative way to accomplish this is:

    String notes = "there are seventeen words in this list make the most of them because it really counts here";

    Int32 one_third = notes.Trim().Split(' ').Count() / 3 + 1;
    String matchString = @"^(\w+\b.*?){" + one_third.ToString() + "}";

    String notes1 = Regex.Match(notes, matchString).ToString();
    notes = notes.Remove(0, notes1.Count()).Trim();

    String notes2 = Regex.Match(notes, matchString).ToString();
    notes = notes.Remove(0, notes2.Count()).Trim();

    String notes3 = notes;
NotMe
  • 87,343
  • 27
  • 171
  • 245
  • 17 words are read into the list. I know this absolutely. Yes, it's a round-about ass-backwards way of doing things but I was in a hurry and now the error is irritating the hell out of me. – Keith Salmon May 10 '13 at 23:39
  • I appreciate your insight and advice. Thank you!!! – Keith Salmon May 11 '13 at 16:57
1

Sorry but you're just plain wrong.

The following code in a new console app outputs:

Some  nine  word
note  which  I'm
stretching  here  now

Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackOverflow16491866 {
    class Program {
        static void Main(string[] args) {
            string notes = "Some nine word note which I'm stretching here now ";
            List<string> words = new List<string>();
            string word = "";
            int idx = 0;
            while (idx < notes.Length) {
                word += notes[idx];
                if (notes[idx] == ' ') {
                    words.Add(word);
                    word = "";
                }
                ++idx;
            }
            string notes1 = "";
            string notes2 = "";
            string notes3 = "";

            int one_third = words.Count / 3;
            int two_thirds = (words.Count / 3) * 2;

            int k;
            for (k = 0; k < one_third; k++)
                notes1 += words.ElementAt(k) + ' ';
            for (k = one_third; k < two_thirds; k++)
                notes2 += words[k] + ' ';
            for (k = two_thirds; k < words.Count; k++)
                notes3 += words[k] + ' ';

            Console.WriteLine(notes1);
            Console.WriteLine(notes2);
            Console.WriteLine(notes3);
            Console.ReadLine();

        }
    }
}

In short, your code works when notes is populated correctly.

Note that it requires a trailing space in notes to split into 3x3x3 instead of 2x2x5

The simplified form of your code is...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackOverflow16491866 {
    class Program {
        static void Main(string[] args) {
            string Notes = "Some nine word note which I'm stretching here now";
            List<string> Words = Notes.Split(' ').ToList();
            int WordsPerLine = (int)Words.Count/3;
            string Note1 = string.Join(" ", Words.GetRange(0, WordsPerLine));
            string Note2 = string.Join(" ", Words.GetRange(WordsPerLine, WordsPerLine));
            string Note3 = string.Join(" ", Words.GetRange(WordsPerLine * 2, Words.Count - WordsPerLine * 2 - 1));
            Console.WriteLine("{0}\n{1}\n{2}", Note1, Note2, Note3);
            Console.ReadLine();
        }
    }
}
Basic
  • 26,321
  • 24
  • 115
  • 201
  • Yup, it was my fault. I was so caught up on why this code wasn't working with a known string that I completely forgot about my initial call to it with an empty string. Geez... sorry for wasting your time trying to figure what the hell I did, but I appreciate the advice you've given as well as the simplified answer. – Keith Salmon May 11 '13 at 17:01
  • No worries :) Glad you got it resolved. – Basic May 11 '13 at 18:30