PDA

View Full Version : Javascript error from logic problem

keyboard
06-10-2013, 06:23 AM
Hey all :)
I decided to work on my problem solving skills so I used one of the Google Code-In challenges as a practice.
For some reason this script just pure and simple doesn't work.
What it should do is -
Get first line of input (number of 'grids')
Each grid has two numbers above it. Grid size and K (ignore the second for now).

For the moment I'm just trying to separate it into an array which stores each object (the object holding the grid and the size of the grid).

EG -

2
7 3
.......
.......
.......
...R...
...BB..
..BRB..
.RRBR..
6 4
......
......
.R...R
.R..BB
.R.RBR
RB.BBB
4 4

That's the sample input I'm working with and this is my source -

<!DOCTYPE html>
<html>
<head>
<script src="jquery-1.10.1.min.js"></script>
<script type="text/javascript">
\$(function() {
\$('#g_go').click(function() { //On click the button
var game_grid = function(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
};
var input = \$('#g_input').text(); //Get the input
input = input.split('\n'); //Split by new line
var counter = 0; //Holds current line of array
var num_grids = input[counter]; counter += 1; //Number of grids, move counter to the next line.
var grid_array = new Array(); //Array to hold the object for each grid
for(i=0;i<num_grids-1;i++) { //Loop through grids
\$('#g_g').append(counter + '~ ');
var grid_data = input[counter].split(' '); //Holds the N, K variables
counter += 1;
grid_array[i] = new game_grid(input.slice(counter, counter + grid_data[0]).join(' '), grid_data[0], grid_data[1]);
\$('body').append(grid_array[i].size + '~~~ <br />' + grid_array[i].contents + '- <br />');
counter += grid_data[0];
}
});
});
</script>
<style type="text/css">
#g_g {
color:red;
}
</style>
</head>
<body>
<textarea id="g_input" cols="70" rows="20"></textarea>
<div id="g_g">Test: </div>
<br/>
<input type="button" id="g_go" value="Go" />
<br />
</body>
</html>

For some reason it prints

Test: 1~ 27~

7~~~
....... ....... ....... ...R... ...BB.. ..BRB.. .RRBR.. 6 4 ...... ...... .R...R .R..BB .R.RBR RB.BBB-

Then I get the error

SCRIPT5007: Unable to get property 'split' of undefined or null reference
base.js, line 15 character 4

Which references var grid_data = input[counter].split(' '); //Holds the N, K variables

I realise this is a complex question so I was wondering if anyone had any ideas for debugging the code :\

jscheuer1
06-10-2013, 12:02 PM
To make a long story short:

\$(function() {
function game_grid(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
}
\$('#g_go').click(function() { //On click the button
var input = \$('#g_input').val(); //Get the input
input = input.split('\n'); //Split by new line
var num_lines = input.length; //Number of lines
var grid_array = []; //array to hold the object for each grid
var i = -1, counter = 0;
while(++i < num_lines) { //Loop through lines
\$('#g_g').append(i + '~ ');
var grid_data = input[i].split(' '); //Holds the N, K variables if
if(grid_data.length === 2 && i + +grid_data[0] < num_lines){ //its length is 2, if its N is represented in the input, then
grid_array[counter] = new game_grid(input.slice(++i, i = i + +grid_data[0]), grid_data[0], grid_data[1]);
--i; //set i back one so incrementing it in the while will be the next line
\$('body').append(grid_array[counter].size + '~~~ <br />' + grid_array[counter].contents.join('<br>') + '<br> ------- <br />');
++counter; //advance counter for the nest grid, if any
}

}
});
});

For details, read the following and following posts:

First off, it didn't do anything, that's in Chrome and the Fox, until I changed:

var input = \$('#g_input').text(); //Get the input

to (note: .text() will work in IE here, all others appear to require the more precise .val() method):

var input = \$('#g_input').val(); //Get the input

After that there was no error (IE 9+ was the same, with text or val) and the output was like you said.

If then I changed:

var num_grids = input[counter];

to what seemed more logical to me:

var num_grids = input.length;

Then I got your exact error. If I then went on to change:

var grid_data = input[counter].split(' '); //Holds the N, K variables

to:

var grid_data = input[counter]? input[counter].split(' ') : 'Out of Range'; //Holds the N, K variables

The error went away and I got a lot more output. That was in Chrome only at that point.

I used 'Out of Range' because that's exactly what is happening there, we start at 0 for the array, but counter is already at 1. By the time we get to the end, counter is out of range.

jscheuer1
06-10-2013, 04:28 PM
OK, looking at this further, without knowing what output you're expecting, this looks like it does things fairly well:

\$(function() {
\$('#g_go').click(function() { //On click the button
var game_grid = function(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
};
var input = \$('#g_input').val(); //Get the input
input = input.split('\n'); //Split by new line
var num_grids = input.length;//Number of grids
var grid_array = []; //Array to hold the object for each grid
for(var i=0;i<num_grids;i++) { //Loop through grids
\$('#g_g').append(i + '~ ');
var grid_data = input[i].split('') //Holds the N, K variables
grid_array[i] = new game_grid(input.slice(i, i + 1).join(''), grid_data[0], grid_data[1]);
\$('body').append(grid_array[i].size + '~~~ <br />' + grid_array[i].contents + '- <br />');
}
});
});

The .split('') and .join('') can be removed for any browser that treats strings more like arrays (that is use the string[#] notation to return the character at that position in the string), most do. IE 7 and less do not. There might be others in other browser families, none too recent.

If this doesn't give the sort of result you were expecting/wanted, please be specific as to what the desired result might be.

Added Later:

I got rid of the tortured:

input.slice(counter, counter + grid_data[0]).join(' ')

because with slice, both parameters are expected to be integers and this was not producing one for the second parameter.

I also got rid of counter in favor of i, as the two didn't seem all that different once I got things working. However, if there is a need for a separate counter, one could be established. Or if you don't need a separate one, i can be replaced with the word counter, as that's what it is here.

Essentially the same, this is more how I would write the above code:

\$(function() {
function game_grid(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
}
\$('#g_go').click(function() { //On click the button
var input = \$('#g_input').val(); //Get the input
input = input.split('\n'); //Split by new line
var num_grids = input.length; //Number of grid lines
var grid_array; //var to hold the object for each grid line while processing
var i = -1;
while(++i < num_grids) { //Loop through grids
\$('#g_g').append(i + '~ ');
var grid_data = input[i].split(''); //Holds the N, K variables - I don't get this comment
grid_array = new game_grid(input[i], grid_data[0], grid_data[1]);
\$('body').append(grid_array.size + '~~~ <br />' + grid_array.contents + '- <br />');
}
});
});

jscheuer1
06-10-2013, 05:35 PM
Ah, there was that one comment I didn't understand, so I went back and read what you wrote before, now it makes sense:

\$(function() {
function game_grid(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
}
\$('#g_go').click(function() { //On click the button
var input = \$('#g_input').val(); //Get the input
input = input.split('\n'); //Split by new line
var num_grids = input.length; //Number of grid lines
var grid_array = []; //array to hold the object for each grid line while processing
var i = -1, counter = 0;
while(++i < num_grids) { //Loop through grids
\$('#g_g').append(i + '~ ');
var grid_data = input[i].split(' '); //Holds the N, K variables
if(grid_data.length === 2){
if(grid_array[counter - 1]){
\$('body').append(grid_array[counter - 1].size + '~~~ <br />' + grid_array[counter - 1].contents + '- <br />');
}
grid_array[counter++] = new game_grid([], grid_data[0], grid_data[1]);
} else if(grid_array[counter - 1]){
grid_array[counter - 1].contents.push(input[i]);
}

}
});
});

However, the above logic depends upon there being an unused 'empty' grid at the end (the 4 4 one in this case). I'm sure there's a way to deal with it without that. We could use the size to dictate how long we add to the contents perhaps. I assume the 2 at the beginning is how many actual grids there are, is that so? If so, perhaps that could be used as well.

Doesn't seem to need that 2 at the top (it could be used to limit the number of grid objects returned), try this:

\$(function() {
function game_grid(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
}
\$('#g_go').click(function() { //On click the button
var input = \$('#g_input').val(); //Get the input
input = input.split('\n'); //Split by new line
var num_lines = input.length; //Number of lines
var grid_array = []; //array to hold the object for each grid
var i = -1, counter = 0;
while(++i < num_lines) { //Loop through lines
\$('#g_g').append(i + '~ ');
var grid_data = input[i].split(' '); //Holds the N, K variables if
if(grid_data.length === 2 && i + +grid_data[0] < num_lines){ //its length is 2, if its N is represented in the input, then
grid_array[counter] = new game_grid(input.slice(++i, i = i + +grid_data[0]), grid_data[0], grid_data[1]);
--i; //set i back one so incrementing it in the while will be the next line
\$('body').append(grid_array[counter].size + '~~~ <br />' + grid_array[counter].contents.join('<br>') + '<br> ------- <br />');
++counter; //advance counter for the nest grid, if any
}

}
});
});

keyboard
06-10-2013, 11:54 PM
For details, read the following and following posts:

First off, it didn't do anything, that's in Chrome and the Fox, until I changed:

var input = \$('#g_input').val(); //Get the input
After that there was no error (IE 9+ was the same, with text or val) and the output was like you said.

Ahhh thanks for that

If then I changed:

var num_grids = input[counter];
to what seemed more logical to me:

var num_grids = input.length;
Then I got your exact error. If I then went on to change:

But then that would break the logic.
I'm trying to assign the number of grids in the input to the variable.
That data is the very first line of the input.
That would just get the number of lines.

var grid_data = input[counter].split(' '); //Holds the N, K variables
to:

var grid_data = input[counter]? input[counter].split(' ') : 'Out of Range'; //Holds the N, K variables
The error went away and I got a lot more output. That was in Chrome only at that point.

I used 'Out of Range' because that's exactly what is happening there, we start at 0 for the array, but counter is already at 1. By the time we get to the end, counter is out of range.

Thanks for that!

However, the above logic depends upon there being an unused 'empty' grid at the end (the 4 4 one in this case). I'm sure there's a way to deal with it without that. We could use the size to dictate how long we add to the contents perhaps. I assume the 2 at the beginning is how many actual grids there are, is that so? If so, perhaps that could be used as well.

Doesn't seem to need that 2 at the top (it could be used to limit the number of grid objects returned), try this:

The 4, 4 bit was an accident actually it shouldn't be there. The actual input is too large for the forum so I just copied a sample. (I accidently copied the start of the next line)

Not sure how to fix those two issues I mentioned in your code. I always struggle to read other peoples stuff :\

jscheuer1
06-11-2013, 12:41 AM
You should try (also posted at the beginning of my responses and at the end of my previous one):

\$(function() {
function game_grid(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
}
\$('#g_go').click(function() { //On click the button
var input = \$('#g_input').val(); //Get the input
input = input.split('\n'); //Split by new line
var num_lines = input.length; //Number of lines
var grid_array = []; //array to hold the object for each grid
var i = -1, counter = 0;
while(++i < num_lines) { //Loop through lines
\$('#g_g').append(i + '~ ');
var grid_data = input[i].split(' '); //Holds the N, K variables if
if(grid_data.length === 2 && i + +grid_data[0] < num_lines){ //its length is 2, if its N is represented in the input, then
grid_array[counter] = new game_grid(input.slice(++i, i = i + +grid_data[0]), grid_data[0], grid_data[1]);
--i; //set i back one so incrementing it in the while will be the next line
\$('body').append(grid_array[counter].size + '~~~ <br />' + grid_array[counter].contents.join('<br>') + '<br> ------- <br />');
++counter; //advance counter for the nest grid, if any
}

}
});
});

I'm sure it's very close to what you want, if not exactly. If it's not exactly, then I'm sure it can be tweaked.

keyboard
06-11-2013, 12:59 AM
You should try (also posted at the beginning of my responses and at the end of my previous one):

\$(function() {
function game_grid(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
}
\$('#g_go').click(function() { //On click the button
var input = \$('#g_input').val(); //Get the input
input = input.split('\n'); //Split by new line
var num_lines = input.length; //Number of lines
var grid_array = []; //array to hold the object for each grid
var i = -1, counter = 0;
while(++i < num_lines) { //Loop through lines
\$('#g_g').append(i + '~ ');
var grid_data = input[i].split(' '); //Holds the N, K variables if
if(grid_data.length === 2 && i + +grid_data[0] < num_lines){ //its length is 2, if its N is represented in the input, then
grid_array[counter] = new game_grid(input.slice(++i, i = i + +grid_data[0]), grid_data[0], grid_data[1]);
--i; //set i back one so incrementing it in the while will be the next line
\$('body').append(grid_array[counter].size + '~~~ <br />' + grid_array[counter].contents.join('<br>') + '<br> ------- <br />');
++counter; //advance counter for the nest grid, if any
}

}
});
});

I'm sure it's very close to what you want, if not exactly. If it's not exactly, then I'm sure it can be tweaked.

I don't get any errors but I'm not getting the right output.
What I want it to do is get the two N,K lines and display the line number (that's the red bit) and display the two grid objects separately

jscheuer1
06-11-2013, 01:40 AM
Well, it's already displaying the 2 grid objects separately in the black part appended to the body below the Go button. With this tweak the red part will show:

Test:
1~ 7 3
9~ 6 4

That's the line number followed by the N and K value.

\$(function() {
function game_grid(a, b, c) { //Function to create the objects
this.contents = a;
this.size = b;
this.win_length = c;
}
\$('#g_go').click(function() { //On click the button
var input = \$('#g_input').val(); //Get the input
input = input.split('\n'); //Split by new line
var num_lines = input.length; //Number of lines
var grid_array = []; //array to hold the object for each grid
var i = -1, counter = 0;
while(++i < num_lines) { //Loop through lines
var grid_data = input[i].split(' '); //Holds the N, K variables if
if(grid_data.length === 2 && i + +grid_data[0] < num_lines){ //its length is 2, if its N is represented in the input, then
\$('#g_g').append('<br>' + i + '~ ' + input[i]);
grid_array[counter] = new game_grid(input.slice(++i, i = i + +grid_data[0]), grid_data[0], grid_data[1]);
--i; //set i back one so incrementing it in the while will be the next line
\$('body').append(grid_array[counter].size + '~~~ <br />' + grid_array[counter].contents.join('<br>') + '<br> ------- <br />');
++counter; //advance counter for the nest grid, if any
}

}
});
});