Javascript dynamic nested object - javascript

My goal is to generate serialized objects in the main object.
A solar system is my idea of nesting where I can access a variable by calling it like
universe.system1.planet4.x
universe > system > planet > etc
Im stuck on generating anything thats nested. So far I can only get 1 level of nesting to work correctly.
setInterval(onTimerTick, 1000);
function onTimerTick() {
var entityCount=4;
for (i=1; i<entityCount;i++){
console.log('system' + i)
universe['planet'+i]=[entityCount,entityCount,entityCount,entityCount];
}//entitycounts in object are placeholder for more data
console.log(universe);
}
var universe = {
}
Output
Object {
system0: [5, 5, 5, 5],
system1: [5, 5, 5, 5],
system2: [5, 5, 5, 5],
system3: [5, 5, 5, 5]
}
Anytime I try to add any nesting it won't generate.

Is this something like this?
You would have to create X nested loops, with X being the number of nested levels you want to create.
const nbSystem = 5;
const nbPlanetPerSystem = 3;
// Create the base
const universe = {};
// Generate systems
for (let i = 0; i < nbSystem; i += 1) {
universe[`system${i + 1}`] = {};
// Generate planets
for (let j = 0; j < nbPlanetPerSystem; j += 1) {
universe[`system${i + 1}`][`planet${j + 1}`] = 'something';
}
}
console.log(universe);
console.log(universe.system3.planet3);

Related

Why my code to clone an array doesn't work JavaScript

So since JavaScript doesn't let you copy array using "=" I have to write this code. But it doesn't work
let arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
var arrclone = [];
for (let x = 0; x < arr.length; x++) {
for (let y = 0; y < arr[x].length; y++) {
arrclone[x][y] = arr[x][y];
}
}
console.log(arrclone);
It said
arrclone[x][y] = arr[x][y];
^
TypeError: Cannot set property '0' of undefined.
How is it undefined both of them are already been declared.
Sorry, I am a beginner sorry if my question seems stupid.
When you start, each row of the array is empty. So initialise it to an empty row of array:
for (let x = 0; x < arr.length; x++) {
// Use this...
arrclone[x] = []; // ========================> You missed this line.
for (let y = 0; y < arr[x].length; y++) {
arrclone[x][y] = arr[x][y];
}
}
FYI, for the right approach, please use Array.slice() (but only for primitive values, see the problem here).
You should ideally be using Array.slice() for copying arrays:
let arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
var arrclone = arr.map(e => e.slice());
console.log(arrclone);
Javascript is not a language that makes copies on assignment. It uses references and stores them instead. Here you are literally just copying reference to the data instead of actual data. There are multiple ways to help you with this.
The most simple way is to use
var arrclone = JSON.parse(JSON.stringify(arr))
We can also use array destructuring and it will look something like this:
var arrclone = [...arr]

How to write my reverse function properly?

i have a problem and i need help for this question.
My reverse function doesn't work the way I want it to.
function reverseArrayInPlace(array){
let old = array;
for (let i = 0; i < old.length; i++){
array[i] = old[old.length - 1 - i];
};
};
let arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
I expect on [5, 4, 3, 2, 1] but i have [5, 4, 3, 4, 5].
Why does it work this way? Please help me understand.
P.S I know about the reverse method.
Variable, with assigned object (array, which is a modified object in fact) - stores just a link to that object, but not the actual object. So, let old = array; here you just created a new link to the same array. Any changes with both variables will cause the change of array.
(demo)
let arr = [0,0,0,0,0];
let bubu = arr;
bubu[0] = 999
console.log( arr );
The simplest way to create an array clone:
function reverseArrayInPlace(array){
let old = array.slice(0); // <<<
for (let i = 0; i < old.length; i++){
array[i] = old[old.length - 1 - i];
};
return array;
};
console.log( reverseArrayInPlace( [1, 2, 3, 4, 5] ) );
P.s. just for fun:
function reverseArrayInPlace(array){
let len = array.length;
let half = (len / 2) ^ 0; // XOR with 0 <==> Math.trunc()
for( let i = 0; i < half; i++ ){
[ array[i], array[len - i-1] ] = [ array[len - i-1], array[i] ]
}
return array;
};
console.log( reverseArrayInPlace( [1, 2, 3, 4, 5] ) );
If you're writing a true in place algorithm, it's wasteful from both a speed and memory standpoint to make an unnecessary copy (as other answers point out--array and old are aliases in the original code).
A better approach is to iterate over half of the array, swapping each element with its length - 1 - i compliment. This has 1/4 of the iterations of the slice approach, is more intuitive and uses constant time memory.
const reverseArrayInPlace = a => {
for (let i = 0; i < a.length / 2; i++) {
[a[i], a[a.length-1-i]] = [a[a.length-1-i], a[i]];
}
};
const a = [1, 2, 3, 4, 5];
reverseArrayInPlace(a);
console.log(a);
Since this is a hot loop, making two array objects on the heap just to toss them out is inefficient, so if you're not transpiling this, you might want to use a traditional swap with a temporary variable.
function reverseArrayInPlace(a) {
for (var i = 0; i < a.length / 2; i++) {
var temp = a[i];
a[i] = a[a.length-i-1];
a[a.length-i-1] = temp;
}
};
var a = [1, 2, 3, 4];
reverseArrayInPlace(a);
console.log(a);
You have in old variable the same array (not by value, but by reference), so if you change any of them you'll have changes in both.
So you should create new array, make whatever you want with them and return it (or just copy values back to your origin array if you don't want to return anything from your function).
It happend like that because:
1) arr[0] = arr[4] (5,2,3,4,5)
2) arr[1] = arr[3] (5,4,3,4,5)
....
n) arr[n] = arr[0] (5,4,3,4,5)
So you can just make a copy (let old = array.slice()) and it'll be woking as you'd expect.

JavaScript - Is there a way to store things like "var x === var y" and "var c === var" in an array form

So I'm basically making some logic code for my tic tac toe game, and I'd like to write down all the states which would mean that somebody has won.
For example;
cell1.innerHTML = cell2.innerHTML = cell3.innerHTML //as one group
So when all of cell 1, 2 and 3 contain the same value, in this case X or O, it becomes true, and alerts the user. I'd like to store all the possible combinations of cells (that would be equal to end the game) in an array like configuration. I'm very sorry if I happen to annoy you with my super noob question or if no such way exists, I'm very new to programming. Thanks in advance!
P.S, I've started to learn jQuery, so if such a solution exists with jQuery, I'll be more than happy to learn it :).
Maybe store the conditions as coordinates in an array. Then loop through the array to find if one of those matches.
var winconditions = [
[{x: 1, y: 1}, {x:2, y:1,}, {x:3, y:1}], //top row
...
];
for(var i = 0; i < windconditions; i++){ //loop every condition
var conditionMatches = true; //will stay true if every coordinate matched
for(var y = 0; y < winconditions[i]; y++){ //loop every coordinate of a condition
var coordinate = winconditions[i][y];
if(!isCoordinateMarked(coordinate){ //some check if coordinate was set by a player
conditionMatches = false;
}
}
if(conditionMatches){
//you found a match!
}
}
maybe this will help you,
i set an array of arrays (matrix), that contain all the possibilities for winning.
and the gameBoard will be a matrix too, of all the elements you have from the html.
var checkWinner = function(gameBoard) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i]; // set values from lines[i] to var a, b, c.
// checking if the first element no null, then check if all equils
if (gameBoard[a] && gameBoard[a] === gameBoard[b] && gameBoard[a] === gameBoard[c]) {
return "win";
}
}
return null;
}

Adding Similar Items Together in a Javascript Array

I'm trying to loop through an array in order to group and count totals.
For example:
var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];
I would like to go through and first see 'Cats', then add all the cat values up, then repeat the process for other unique categories.
The final output would be:
Cats (7)
Mice (2)
Dogs (5)
Currently, I'm trying to accomplish it this way, but I'm obviously making a rookie mistake somewhere.
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var animalCounter = function(array){
var list = '';
for(var i = 0; i<array.length; i++){
var animalID = array[i][0];
var animalCount = 0;
for (var x; x<array.length; x++){
if(animalID == array[x][0]){
animalCount += array[x][0] - 1;
}
list += animalID + " (" + animalCount + ")\n";
}
}
alert(list);
}
animalCounter(farm);
use an object to add similar animals together
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var o = {};
for (var i=farm.length; i--;) {
var key = farm[i][0],
val = farm[i][1];
o[key] = key in o ? o[key] + val : val;
}
FIDDLE
Then it's easy to create the list
for (var key in o) {
list += key + " (" + o[key] + ")\n";
}
FIDDLE
You're missing an outer layer of brackets:
var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];
What you had will end up being the same as
var farm = ['Dogs', 5];
The comma operator does strange things, especially in a var statement because , also separates individual variable declarations and initializations.
I'd probably do it a bit differently:
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var animalCounter = function(array){
var animalObject = {};
for(var i = 0; i<array.length; i++){
var animalID = array[i][0];
var animalCount = array[i][1];
if(animalObject[animalID]) {
animalObject[animalID] += animalCount;
} else {
animalObject[animalID] = animalCount;
}
}
return animalObject;
}
The first function, animalCounter, creates an object that maps animal names to the numbers in the array. So for your example, it will return an object that looks like this:
{ 'Cats': 7, 'Mice': 2, 'Dogs': 5 }
From there, once you have the object, it's trivial to create a string to output this data in whatever format you like:
var counter = animalCounter(farm);
var list = '';
for(var key in counter) {
list += key + ': ' + counter[key] + ', ';
}
console.log(list); //=> Cats: 7, Mice: 2, Dogs: 5,
The reason your initial function didn't work is because it didn't take into account that there might be more than one instance of the same animal in your array. If your original array was [['Cats', 7], ['Mice', 2], ['Dogs', 5]] it would have been fine, but because your Cats entry was split into ['Cats', 4], ['Cats', 3], your original code saw that as two distinct animals. Notice in my function:
if(animalObject[animalID]) {
animalObject[animalID] += animalCount;
} else {
animalObject[animalID] = animalCount;
}
The code checks to see if the animal is already stored in the new object, and if it is, increments the count, rather than creating a new counter from 0. This way it deals with any duplicates.
I see three problems:
setting animalCounter equal to the number of animals - the new number of animals replaces whatever might already have been stored in animalCounter, so nothing is added up here
creating the animalCounter variable within the loop - if var animalCount is inside the loop, then you actually have a completely new variable for each element of the array
using a single variable for all the types of animals
Instead, try this:
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
var animalCounter = function (array) {
var list = '',
catsCount = 0,
miceCount = 0,
dogsCount = 0;
for (var i = 0; i < array.length; i++) {
var animalID = array[i][0];
var animalCount = array[i][1];
if (animalID === 'Cats') {
catsCount += animalCount;
} else if (animalID === 'Mice') {
miceCount += animalCount;
} else if (animalID === 'Dogs') {
dogsCount += animalCount;
}
}
list = 'Cats(' + catsCount + ') Mice(' + miceCount + ') Dogs(' + dogsCount + ')';
alert(list);
}
animalCounter(farm);
There are separate variables for each type of animal, and the value in the array is added onto the correct counter variable.
Or, for a more organized solution:
var farm = []; farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2],
['Dogs', 5]);
var animalCounter = function (array) {
var list = '',
animalCounters = {};
for (var i = 0; i < array.length; i++) {
var animalID = array[i][0];
var animalCount = array[i][1];
animalCounters[animalID] = (animalCounters[animalID] || 0) + animalCount;
}
for (var id in animalCounters) {
list += id + " (" + animalCounters[id] + ")\n";
}
alert(list);
} animalCounter(farm);
In this code, the animalCounters variable is an object. JavaScript objects act like associative arrays, which lets us use the animal ID string as a "key" to an integer that is the animal sum. Each type of animal is a property of the animalCounters object, with the sum for that type of animal as its value.
I used some slightly obscure notation here, so I'll explain.
animalCounters[animalID]
This is just a different method of referring to properties of an object. In JavaScript, animalCounters.Cats and animalCounters["Cats"] access the same thing. But, if you don't know for sure that the type of animal will be Cats, you need "Cats" (or whatever other kind of animal) to be in a variable. The animalCounters["Cats"] notation takes a string, so you can say this and it will work:
var animalID = "Dogs";
alert(animalCounters[animalID);// returns animalCounters.Dogs
animalCounters[animalID] = (animalCounters[animalID] || 0) + animalCount;
Here, the (animalCounters[animalID] || 0) is saying that if animalCounters[animalID] already has a value, add that value to animalCount, otherwise add 0 to animalCount. This is necessary because if you try to add animalCounters[animalID] to animalCount before animalCounters[animalID] has been set to anything, the addition won't work right.
Just for funsies... (not very practical)
var farm = [['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]];
farm.reduce(function(a, b, i){
var r = (i==1 ? a.slice() : a),
j = r.indexOf(b[0]);
if(j >= 0){
r[j+1] += b[1];
return r;
} else return r.concat(b);
}).reduce(function(a, b, i){
return i%2 ? a+' ('+b+')' : a+'\n'+b;
});
Rough explanation:
Iterate over each element of farm reducing the 2D array to a flat array where every odd index is the "count" that corresponds to the previous element - taking note to check if the "key" in the even index already exists in the array (in which case update it's count respectively). The slice call is in there just to make sure that the original array is left unmodified. This results in an array looking like:
["Cats", 7, "Mice", 2, "Dogs", 5]
This new array is reduced once more, this time concatenating each element into a single string - formatting dependent on whether or not the current iteration has an odd or even index.
Array.reduce is one of those functions that isn't supported in older browsers (if that is important) but there's a polyfill available on the MDN site.
When you access the amount of animals of a certain kind you made a simple mistake:
animalCount += array[x][0] - 1;
farm[x][0] will always return the animal's name which is a string, so when trying to subtract 1 from it it will result in NaN (Not a Number).
Also the first for loop: for(var i; i<array.length; i++){ ... cycles through all the array slots even if they were already counted, so cats would be counted twice so instead of cats counted as 7 they would amount to 14.
You need to create a copy of array and take off the slots already counted. The tricky part is copying the array by value and so that any changes to Temp won't change farm (see Copying Arrays):
var farm = [];
farm.push(['Cats', 3], ['Cats', 4], ['Mice', 2], ['Dogs', 5]);
function countAnimals(array) {
var Temp = [];
var d = 0;
//This while loop copies the array
while (d < farm.length) {
var s = array[d].toString();
Temp.push(s.split(","));
d++;
}
var list = "";
var done = 0;
while (done < array.length) {
if(Temp[0][1] == "Done") {
Temp.shift();
} else {
var animalID = Temp[0][0];
var count = parseFloat(Temp[0][1]);
Temp.shift();
var i = 0;
while (i < Temp.length) {
if(Temp[i][0] == animalID) {
count = count + parseFloat(Temp[i][1]);
Temp[i][1] = "Done";
}
i++;
}
list = list + "\n" + animalID + "("+count+")";
}
done++;
}
alert(list);
}
countAnimals(farm);

Javascript: Multiple Array looping

TL DR; What is the most efficient way to sort and compare values in a multiple arrays?
Okay, so we'll assume a few constants to make this whole thing simple
var a = [1, 2, 3, 4, 5, 6], b = [0, 9, 8, 7, 6, 88, 99, 77], i, j;
Now if I wanted to see if any value in a is equal to any other value in b I'd have to sort through one of these arrays 6 times. That's a lot of work, and it would seem there should be a more efficient way to do this. For those needing a visual aide here you are ( and yes I know about -- and ++ I just don't like to use 'em ):
for (i = a.length - 1; i > -1; i -= 1) {
for (j = b.length - 1; j > -1; j -= 1) {
if (a[i] === b[j]) {
return b[j];
}
}
}
The B array gets ran through once for EACH element in A. Again, certainly there is a more efficient way to complete this task?
-Akidi
Depends on the size of your input arrays (several tradeoffs there)-- your nested loops are simplest for small inputs like your examples.
If you have huge arrays, and have control over their construction, consider keeping a map (Object in JS) around acting as a lookup set (if you're creating a in a loop anyways, you can build the set in parallel.)
var setA = {};
for (int i = 0; i < a.length; i++) {
setA[a[i]] = true;
}
Then you can see whether something exists in the set by just checking setA[?].
Likewise with B, or with both together, etc, depending on your needs.
Maybe something like this may help?
var a = [1, 2, 3, 9, 5, 0], b = [0, 9, 8, 7, 6, 88, 99, 77];
var a_dict = {}, l = a.length;
for (var i=0; i < l; i++) {
a_dict[a[i]] = 1;
}
l = b.length;
for (var i=0; i < l; i++) {
if(!!a_dict[b[i]]){
console.log(b[i]);
}
}
You can convert one array to "dict-like" object and compare others with it...

Categories

Resources