JS arrays 3 deep getting lost - javascript

Learing JS by game. and of course the simplist is a clicker :-)
OK, so I have had lots of help with this, but the array section is just not sinking in. JS arrays do not work like arrays I am used to. two part question:
1. Why does the following code keep saying weaponlevelIfo[0] undefined, when it is defined? Please explain your answer, don't just correct mine LOL
2. I am more interesting in populating the code at runtime
As stated, all the research I am coming across as well as videos, talk about static data, i.e. it is put in at programing level, not run time.
I have had a really patient community person that has tried to help me understand JS arrays, but I must be blind as I am not seeing it. I can do in in other language just fine. but JS? nope.
// produces weaponLevelNfo[weaponId][level][cost] and [goldperclick]
// weapon, level 1-9, cost/goldperclick on each level
var weaponLevelNfo = new Array(14); // Outter array comprised of weapons 0-14
function initGame() {
for (let i=0; i <= weaponLevelNfo.length; i++) {
weaponLevelNfo[i] = new Array(9); // create leves array under weaponid array
for (let j = 0; j < weaponLevelNfo[i].length; j++) {
// loop through each 9 levels changing as needed
weaponLevelNfo[i][j] = new Array(2); // create an object for readability
}
}
}
initGame();// added - forgot to add this in the original post (sorry)
weaponLevelNfo[0][0][2]=3;
console.log(weaponLevelNfo[0][0][2]);
// always gives me weaponLevelNfo[0] not defined
I prefer the results to be as such
weaponLevelNfo[x][y].cost,
weaponLevelNfo[x][y].incomePerClick,
but am quite happy with
weaponLevelNfo[x][y][z],
weaponLevelNfo[x][y][z],
But as you can see from the code, assigning them direct or at runtime, I get the not defined error
What is missing to allow me to assign these at run time?

Two issues:
You need to call initGame to create all those subarrays, otherwise weaponLevelNfo[0] is not defined and so weaponLevelNfo[0][0] will trigger the error you get.
Your outer loop performs one iteration too many (<=). Change:
for (let i=0; i <= weaponLevelNfo.length; i++) {
by
for (let i=0; i < weaponLevelNfo.length; i++) {
Without that change, the last iteration is actually adding an element to the array in slot i, and so the length of the array increases... the loop becomes infinit.
Note that there are shorter ways to define such a nested array. For instance:
var weaponLevelNfo = Array.from({length:14}, () => Array.from({length:9}, () => [0, 0]));

When you create an array in javascript with new Array(2) this is an array with two positions. In this example (new Array(2)), you can access it at index 0 and index 1. Consider the following:
var newArr = new Array(2);
You can then access the two positions by the following:
var position1 = newArr[0];
var position2 = newArr[1];
So when you try this:
var position = newArr[2];
This will throw an undefined exception.
You can change the end of your example code to this:
weaponLevelNfo[0][0][1]=3;
console.log(weaponLevelNfo[0][0][1]);

You define array with two elements Array(2) with indexes 0 and 1 but you use index 2.
To initialize multidimensional array filled by zeros (if not remove .fill(0)) use
[...Array(14)].map(x=>[...Array(9)].map(y=>Array(2).fill(0)));
let a=[...Array(14)].map(x=>[...Array(9)].map(y=>Array(2).fill(0)));
a[13][8][1]=3;
console.log('Last element value:',a[13][8][1]);
console.log(JSON.stringify(a));

Related

updating a JSON objects value with a for loop in Java Script

I have an array of 11 objects which contain JSON data. I wrote a function in which a new key with a zero value is added to each of the objects. Now I want to update the value of the said key in all 11 objects. The data is stored in an array2 with 11 numbers. My for loop doesn't seem to work for this, and the only way to do it (so far) is to hard code it. Does anyone has a suggestion how this can be done?
The desired outcome would be this:
array[0].new_key = array2[0];
array[1].new_key = array2[1];
The first art of the function, before the for loop with j, is for adding the new key into the original array and that part works.
for (i = 0; i < array.length; i++) {
array.map(i => i.new_key = 0);
console.log(array)
for (j = 0; j < array2.length; j++) {
array[i].new_key = array2[j];
console.log(array)
}
}
}```
I split it into two functions, I realized that I made it too complicated and it didn't made sense. I wrote a second function that only updates all the key values, so indeed, I removed the inner loop as it was not needed. Thank you for the help.
.map() does not modify the original array:
The map() method creates a new array with the results of calling a
provided function on every element in the calling array.
You will want to get the result of the map by assigning it to a variable, and see what is happening there. Right now you don't do anything with it, so it will just disappear.
While the above is true for maps, in this case the original array is being modified as we access the object's properties and modify them there.

Difference between initializing array length and assign to index vs repeatedly pushing

While working on building a list of sheet names, I came across this question:
List names of sheets in Google Sheets and skip the first two
Saving you the click, this person's solution is: (Stripped down, pseudo code)
getSheets() // Get all the sheets in spreadsheet workbook
var out = new Array( sheets.length+1 ) ;
for (var i = 1 ; i < sheets.length+1 ; i++ )
out[i] = [sheets[i-1].getName()];
return out
My solution would have leveraged:
...
var sheetName = sheet[i].getName();
out.push(sheetName);
The first solution seems to dynamically create empty array values, then later declare their value. While I have always just pushed new values into the array.
What is the difference?
In which situations is one better than the other?
In which situations should either be avoided?
Your code and the original code do quite different things.
Assuming sheets has objects in it that return the names "sheet1", "sheet2", and "sheet3" from getName, the original code creates an array that looks like this:
[
(missing),
["sheet1"],
["sheet2"],
["sheet3"]
]
Note two things:
There is no entry at index 0. (It literally doesn't exist at all, which is subtly different from existing and containing the value undefined.)
The other entries are single-element arrays, each containing its sheet name.
Your code creates this instead:
[
"sheet1",
"sheet2",
"sheet3"
]
Presumably the author had a reason for skipping index 0 and creating subordinate arrays (I'd guess because they were passing that result array into some API function that expects something in that form).
So there's no really a "better" or "worse" here, just different.
If your fundamental question is whether this:
var original = ["one", "two", "three"];
var updated = [];
for (var i = 0; i < original.length; ++i) {
updated[i] = original[i].toUpperCase(); // Or whatever
}
is better/worse than this:
var original = ["one", "two", "three"];
var updated = [];
for (var i = 0; i < original.length; ++i) {
updated.push(original[i].toUpperCase()); // Or whatever
}
the answer is: It's really a matter of style. Performance isn't markedly different between the two (and rarely matters), and amusingly one way is faster on some JavaScript engines and the other way is faster on others.
Both of those can probably be better expressed using map:
var original = ["one", "two", "three"];
var updated = original.map(function(entry) { return entry.toUpperCase(); });
I think Google Sheets has map, even though it mostly has only ES3-level features otherwise.
Side note: new Array is almost never the right way to create an array.

Adding property in nested for-loop causes same values for each parent for-loop

I have an array containing information on playing cards (cardInfo). A sample of the array is added in the code below. Since each card may have duplicates, I want to use this information to create a deck by pushing its information to a new array (drawPile) for each card of that type (the property 'frequency1').
var common = 4;
var uncommon = 3;
var rare = 2;
var cardInfo = [
{name:'Card A',frequency1:common,frequency2:rare,frequency3:0},
{name:'Card B',frequency1:common,frequency2:uncommon,frequency3:0},
{name:'Card C',frequency1:uncommon,frequency2:uncommon,frequency3:0}
];
var drawPile = [];
for (var cType = 0; cType < cardInfo.length; cType++) {
for (var freq = 0; freq < cardInfo[cType].frequency1; freq++) {
drawPile.push(cardInfo[cType]);
console.log(drawPile.length - 1);
drawPile[(drawPile.length - 1)].id = (drawPile.length - 1);
console.log(drawPile[(drawPile.length - 1)]);
}
}
The resulting console log, however, shows that all 4 "Card A" cards have the id property 3, all 4 "Card B" cards have the id property 7, and all 3 "Card C" cards have the id property 10. It is as if the nested (freq) loop only runs for all .push() commands before it adds the id property.
More strangeness: when I run this code in jsfiddle, I can replicate these results if I first run it and then open the console log, but when I run it after the console lof is already open, it works as intended.
How do I ensure each card gets a unique identifier?
EDIT: It gets even stranger when I get the exact same results if I create a completely new for loop specifically for adding the id property as seen in this code.
As you loop around the cardInfo collection, for each cardInfo instance, you loop around the frequencies. Technically for each frequency you are pulling in the same instance of the cardInfo, so technically you are updating the same cardInfo instance for each frequency iteration. Now your next statement will be but I write console.log for each iteration. But when you debug the code, console.log outputs correctly, however I'm not sure whether this is the case when it is run through without a debug. So how can you resolve this?
After determining that it was modifying the same instance for each iteration of the related frequency, I decided to clone the card (I'm not saying this is a great idea, but it proves the concept). I achieved this by using:
JSON.parse(JSON.stringify(cardInfo[cType]));
Again this is not the most performant way of achieving this, but highlights where I believe the issue lies.
I have applied this change to a fiddle (I've also simplified the code a little) and if you remove the cloning it operates as you currently see it, but with the cloning it works as you would expect it.
I hope that helps
You are pushing object reference to the drawPile array in the loop. That's why all "Card A" cards have the same ID as array contains same reference. you have to clone the object before pushing into the loop. You can make the below change to your code.
for (var freq = 0; freq < cardInfo[cType].frequency1; freq++) {
drawPile.push(JSON.parse(JSON.stringify(cardInfo[cType])));
console.log(drawPile.length - 1);
drawPile[(drawPile.length - 1)].id = (drawPile.length - 1);
console.log(drawPile[(drawPile.length - 1)]);
}

Angularjs for inside Controller running just once

I'm trying to store the ids of an array of objs, but appears that this for loop is running just once, very strange
for (var i = 0; i < softwareIds.length; i++) {
var b = softwareIds[i].id;
}
console.log(b);
console.log(softwareIds);
the last two lines of consoles returns this:
(number 2 is the first console.log returning the last obj id, and [Object, Object, Object] in the second console.log)
I'm using angujarJS, this for are inside a Controller, any idea?
You should declare the array first. see below
$scope.b = [];
for (var i = 0; i < softwareIds.length; i++) {
$scope.b.push(softwareIds[i].id);
}
console.log(b);
Your for loop is working fine but you are every time overwriting the value stored in the variable. You should push id value in an array.
Your loop is running three times. You keep updating the value of b.
The fact that you are using this code inside AngularJS controller doesn't have anything to do with what you are experiencing. This is a common thing, the console.log(b) line of code is going to be executed only when the for loop finishes its own thing and when the for loop is finished b variable is going to have the value of the last items id.
Also, in order to store the array of object ids as you need to use an Array, you pretty much answered on this question.
var ids = [];
for (var i = 0; i < softwareIds.length; i++) {
ids.push(softwareIds[i].id);
}

for..in loop loops over non-numeric indexes “clean” and “remove”

This is something very basic I might be missing here but I haven't seen such result till now.
I have a for loop where options.headers.length is 3. And in for loop I am dynamically creating a table header. Ideally this loop should run three times for 0 1 and 2 but when I have printed index it's printing 0,1,2,clean and remove. I haven't seen clean and remove as indexes. I know this information is not sufficient enough but if you have any clue please suggest. something might be overriding this is all I am concluded too after my debugging.
for (index in options.headers)
if you don't want to iterate clean and remove then change the loop to:
for (var i=0; i< options.headers.length;i++){
//use i for getting the array data
}
if you use for (index in options.headers) it will iterate for non-numeric keys also.
don use just index (as that is = window.index = global = bad) use var index
(read more here https://www.google.pl/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=globals+javascript+bad)
you have to check does the array has it as own property or maybe its some function (more after answer)
for (var index in options.headers) {
if (options.headers.hasOwnProperty(index) {
// code here
}
}
more about #2:
let's say we have
var array = [0,1,2,3];
and besides that, extending array with function (arrays can have functions in javascript and strings too)
Array.prototype.sayHello = function() {
alert('Hello');
};
then your loop would print sayHello as part of the array, but that's not it's own property, only the arrays
I assume that options.headers is an Array?
This happens when you (or some framework you load) adds methods to the Array prototype. The "for in" loop will enumerate also these added methods. Hence you should do the loop for an array with:
for (var i = 0; i < options.headers.length; i++)
That way you will only get the real values instead of added methods.

Categories

Resources