Cleanest way to check all sub-arrays are defined - javascript

I was just writing a little game of life script, seen as I'd never really done it. I have created a matrix array that has sub arrays of columns, each containing the values of each row in that column. When I loop through to check the neighbours I want to count them like the below:
var count = 0;
count += this.data[i-1][j-1];
count += this.data[i-1][i];
count += this.data[i-1][j+1];
count += this.data[i][j-1];
count += this.data[i][j+1];
count += this.data[i+1][j-1];
count += this.data[i+1][i];
count += this.data[i+1][j+1];
Obviously on the i-1 and i+1 indexes will cause a problem at the beginning and end of the arrays. I can check for undefined on the value of the rows (the deepest value) like so:
count += typeof this.data[i-1][j-1] !== 'undefined' ? this.data[i-1][j-1] : 0;
But if the columns array is undefined obviously it will error and I can't check this without writing a few more lines. Is there anyway I can catch this error in this one line, or is there a quicker way to get this data. I just don't want lots of try/catches or if/elses to account for the edges of the matrices?
I know I could remove the sub-array structure and just have one long array of data but I was just curious about efficient ways of dealing with this structure.

"Cleanest" is a subjective term, but three possibilities for you:
Use JavaScript's curiously powerful || operator:
count += (this.data[i-1] || [])[j-1] || 0;
That works for two reasons:
In JavaScript, accessing an array index that doesn't exist returns undefined. It doesn't cause an error.
Unlike many programming languages, JavaScript's || returns its first operand if that operand is truthy and its second operand if not, rather than a boolean. Truthy values are values that aren't falsey; falsey values are 0, undefined, null, "", NaN, and of course false. So 1 || 0 is 1, undefined || 0 is 0, and 0 || 0 is also 0.
Note that the || [] part of that won't create an array unless the this.data[i-1] part is undefined. But if you're worried about creating temporary arrays like that, you could have just one defined throughout your script (var blank = [];) and then use it:
count += (this.data[i-1] || blank)[j-1] || 0;
Define your arrays (both the "vertical" one and all the "horizontal" ones) two elements too big and set the values around the edges to 0. Then loop from 1 to < length - 1, and you don't have to worry about going out of bounds because the -1 case will take you to the 0th element, which has the value 0, and the +1 case will take you to the length-1 element, which also has the value 0. This offers quite clean code at the expense of a small bit of extra memory. Or:
Have a function to get the value which checks the coordinates and returns 0 if either is invalid. This offers quite clean code at the expense of a small bit of added overhead.

Related

What is the function of 'i' in for loop in JavaScript when accessing arrays?

const rapperArray = ['Tupac', 'Jay-Z', 'Notorious B.I.G', 'Kendrick Lamar']
for (let i = 0; i < rapperArray.length; i++) {
console.log(rapperArray[i]);
if (i === 2) {
break;
}
}
console.log("And if you don't know, now you know.");
So how does i work in that console.log( rapperArray[ i ] );? I know that it accesses the elements in the array but I can't seem to get my head around how it actually functions.
The i is a variable. In a for loop, you have the following syntax:
for(initialization; condition; update) {
// for loop body
}
The for loop will begin by running the initialization portion first, which in this case declares the variable i with an initial value of 0. Before each iteration of the loop, condition is checked and the loop body will run as long as condition is true. Then, after the loop body runs, update will run. In this case, the variable i will have its value incremented by 1, i.e. i++ is basically the same thing as i += 1.
In other words, the for loop will use every value of i from 0 up until rapperArray.length - 1. So console.log(rapperArray[i]); will be the same as console.log(rapperArray[0]); in the first iteration, console.log(rapperArray[1]); in the second iteration, and so on.
You can see this more clearly by running the following:
const rapperArray = ['Tupac', 'Jay-Z', 'Notorious B.I.G', 'Kendrick Lamar'];
for (let i = 0; i < rapperArray.length; i++) {
console.log('When i is ' + i + ', rapperArray[i] is ' + rapperArray[i] + '.');
}
A for loop header consists of three parts:
Initial state let i = 0
Condition to check if loop should continue i < length
Increment per run i++
We start with 0, in the beginning i is lower than length, then we up it by 1 (i++ increases i by 1), if it's still lower than length - we continue until i has the same value as length and then we stop.
It indicates the current iteration of the loop. For each iteration, i is incremented. When used to get an array item, it represents the index of the item to retrieve from the array. You can read more about indexed collections here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections
Arrays in JavaScript are zero-index based, which means the first index is always zero. And they increment from there. Arrays are ordered collections, so the item at the first index (zero) is always the same, and so on.
If you have a list on paper in front of you, you may number the lines starting with 1 and going up from there. Each item in an array is somewhat like each line on that piece of paper, only the line numbers start at zero instead of one. If you want to get line n from the paper, you find the line numbered n and there you go.

Javascript for() to add values to array

I have a program that displays errors like this:
wifi : 298
So, it says the type of error and the number.
Right now there are 8 errors, which I have divided by type and number, so 16 values. I would like to add them to an array like:
var array{type:'____', number:'____'}
I would like to add the values via function, so it can be automatic, in case I add ou remove errors to display, a for seems the best way to do it.
Problem is.. I'm horrible at math..
I've tried a for where i starts at -1 and its i++. The type would be value[i+1] and the number would be value[i+2].
This would be the result:
i=-1
type=value[0]
number=value[1]
i=0
type=value[1]
number=value[2]
So you see, value[1] appears twice:
0
1
1
2
When it should only appear once:
0
1
2
3
var errorSplit = errorlog.split(/:|;/);
var errors;
for(var i=-1;i<16;i++){
errors= {type: errorSplit[i+1], num: errorSplit[i+2]};
}
Just up it by 2 on every iteration. I would also suggest slight changes for readability (Replacing -1 by 0 and changing index acccessors).
You need to make the errors array an actual array and add things to it not overwriting them.
You can make the loop variable length too by just using the errorSplit length as a limiter.
var errorSplit = errorlog.split(/:|;/);
var errors=[]; // Make the array an actual array
for(var i=0; i<errorSplit.length; i+=2) { // Start at index 0, go until there are no errorSplit parts left, up it by 2 every iteration
if(errorSplit.length<(i+1)) // If the index is higher than errorSplit+1, exit the loop
break;
errors.push({type: errorSplit[i], num: errorSplit[i+1]}); // Add the element to the array
}

Array.length longer than array

I am getting some strange behaviour out of JavaScript array.length. In particular, I have an array that returns length as 3, when there is only one element in the array, at index 0. I've read this question/answer dealing with incorrect values returned by array.length, but it doesn't answer my question because my array doesn't seem to be associative.
Here's a screenshot of my console, demonstrating the odd behaviour.
The code is throwing an error because I'm looping over the first array using array.length and the code is trying to acccess the second and third elements it thinks should be in the array, and not finding them. Other entries in the database seem to not have this problem (see the second array in the screenshot).
So here's the question: Why is array.length in the first array 3 instead of 1?
The length Array property in javascript is writable from anyone and is not a reliable source to find the number of elements in the array.
Usually, it is safe to assume that the array has length elements in it, but sometime you can have different behaviours. This one is one of them.
var x = [1,2,3];
x.length = 5;
using a for construct will lead to some undefined values
for (var i = 0; i < x.length; i++) {
alert(x[i]);
}
using the forEach Array method would result (on Firefox at least) in the desired behaviour.
x.forEach(function(item) {
alert(item);
});
Note that, if you change the array length to a lesser value than the real value, the array would lose the extra elements and if you restore the original value the extra elements will be lost forever.
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length

Looping over undefined array keys

Problem:
I have a DB containing math exercises, split by difficulty levels and date taken.
i want to generate a diagram of the performance over time.
to achieve this, i loop through the query results, and ++ a counter for the level and day the exercise was taken.
example: level 2 exercise was taken at 01.11.2015.
this.levels[2].daysAgo[1].amountTaken++;
with this, i can build a diagram, where day 0 is always today, and the performance over days is shown.
now levels[] has a predefined amount of levels, so there is no problem with that.
but daysAgo[] is very dynamic (it even changes daily with the same data), so if there was only one exercise taken, it would wander on a daily basis (from daysAgo[0] to daysAgo[1] and so on).
the daysAgo[] between that would be empty (because there are no entries).
but for evaluating the diagram, i need them to have an initialized state with amountTaken: 0, and so on.
problem being: i can't know when the oldest exercise was.
Idea 1:
First gather all entries in a kind of proxy object, where i have a var maxDaysAgo that holds the value for the oldest exercise, then initialize an array daysAgo[maxDaysAgo] that gets filled with 0-entries, before inserting the actual entries.
that seems very clumsy and overly complicated
Idea 2:
Just add the entries this.level[level].daysAgo[daysAgo].amountTaken++;, possibly leaving the daysAgo array with a lot of undefined keys.
Then, after all entries are added, i would loop over the daysAgokeys with
for (var i = 1; i < this.maxLevel; i++) { // for every level
for (var j = 0; j < this.levels[i].daysAgo.length; j++) {
but daysAgo.lengthwill not count undefined fields, will it?
So if i have one single entry at [24], length will still be 1 :/
Question:
How can I find out the highest key in an array and loop until there, when there are undefined keys between?
How can i adress all undefined keys up until the highest (and not any more)?
Or: what would be a different, more elegant way to solve this whole problem altogether?
Thanks :)
array.length returns one higher than the highest numerical index, so can be used to loop though even undefined values
as a test:
var a=[]
a[24]=1
console.log(a.length)
outputs 25 for me (in chrome and firefox).

How does JavaScript [] really work?

I'm writing a JavaScript interpreter for extremely resource-constrained embedded devices (http://www.espruino.com), and every time I think I have implemented some bit of JavaScript correctly I realise I am wrong.
My question now is about []. How would you implement one of the most basic bits of JavaScript correctly?
I've looked through the JavaScript spec and maybe I haven't found the right bit, but I can't find a useful answer.
I had previously assumed that you effectively had two 'maps' - one for integers, and one for strings. And the array length was the value of the highest integer plus one. However this seems wrong, according to jsconsole on chrome:
var a = [];
a[5] = 42;
a["5"]; // 42
a.length; // 6
but also:
var a = [];
a["5"] = 42;
a[5]; // 42
a.length; // 6
So... great - everything is converted into a string, and the highest valued string that represents an integer is used (plus one) to get the length? Wrong.
var a = [];
a["05"] = 42;
a.length; // 0
"05" is a valid integer - even in Octal. So why does it not affect the length?
Do you have to convert the string to an integer, and then check that when converted back to a string, it matches?
Does anyone have a reference to the exact algorithm used to store and get items in an array or object? It seems like it should be very simple, but it looks like it actually isn't!
As the specs said, and was noted by others:
"A property name P (in the form of a String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2^32-1."
That's explain why in your scenario "5" is considered an array index and "05" is not:
console.log("5" === String("5" >>> 0));
// true, "5" is equal to "5", so it's an index
console.log("05" === String("05" >>> 0));
// false, "05" is not equal to "5", so it's not an index
Note: the Zero-fill right shift is the shortest way in JS to have a substitute of ToUint32, shifting a number by zero.
See MDN
It's possible to quote the JavaScript array indexes as well (e.g.,
years["2"] instead of years[2]), although it's not necessary. The 2 in
years[2] eventually gets coerced into a string by the JavaScript
engine, anyway, through an implicit toString conversion. It is for
this reason that "2" and "02" would refer to two different slots on
the years object and the following example logs true:
console.log(years["2"] != years["02"]);
So with a["5"] you are accessing the array while a["05"] sets a property on the array object.
Arrays are just objects. That means they can have additional properties which are not considered elements of the array.
If the square bracket argument is an integer, it uses it to perform an assignment to the array. Otherwise, it treats it as a string and stores it as a property on the array object.
Edit based on delnan's comment and DCoder's comment, this is how JavaScript determines if it is an appropriate index for an array (versus just a property):
http://www.ecma-international.org/ecma-262/5.1/#sec-15.4
Arrays are also objects.
By doing this
a["05"] = 5;
You are doing the same thing as:
a.05 = 5;
However, the above will result in a syntax error, as a property specified after a dot cannot start with a number.
So if you do this:
a = [];
a["05"] = 5;
you still have an empty array, but the property of a named 05 has the value 5.
The number x is an array index if and only if ToString(ToUint32(x)) is equal to x (so in case of "05" that requirement is not met).

Categories

Resources