This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 7 years ago.
I think its supposed to be simple, but somehow, I don't get what I'm doing wrong.
I have this code:
var currentPosition = {
x:10000,
y:10000
};
var directions = "v>v<";
var housesVisited = [{x:10000, y:10000}];
function createHouseCoordinates(data){
for(var x = 0; x<4; x++){
if(data[x]=="^"){
currentPosition.x += 1;
} else if(data[x]=="v"){
currentPosition.x -= 1;
} else if(data[x]==">"){
currentPosition.y += 1;
} else if(data[x]=="<"){
currentPosition.y -= 1;
}
housesVisited.push(currentPosition);
}
}
createHouseCoordinates(directions);
console.log(housesVisited);
It is supposed to take directions in the form of symbols, modify the current location, and create an array of objects that lists all locations that were visited. The result of the above, when I run it, gives me this (basically the end position):
[ { x: 10000, y: 10000 },
{ x: 9998, y: 10000 },
{ x: 9998, y: 10000 },
{ x: 9998, y: 10000 },
{ x: 9998, y: 10000 } ]
I was expecting:
[ { x: 10000, y: 10000 },
{ x: 9999, y: 10000 },
{ x: 9999, y: 10001 },
{ x: 9998, y: 10001 },
{ x: 9998, y: 10000 } ]
What am I doing wrong? What should I read up on to understand this better..?
Thanks in advance!
currentPosition is an object. When you're making changes to it (eg currentPosition.x += 1), you're always changing that same object.
You then push it into an array, but what you're actually pushing is a reference to that object, so all the elements of the array are pointing at the same underlying object.
To fix your code, you need to clone the object information into a new object as you push it into the array:
housesVisited.push({x: currentPosition.x, y: currentPosition.y});
Related
I am creating an HTML5 platform game using objects for collision detection and using a 2d tile map to render the level. That is all working.
I want to use the same 2d array to build the object array dynamically to allow the player to build maps as required and also for ease of creating the maps in the first place. When hardcoding the object array, everything works so I know that the collision detect and game engine work.
While I can create objects for each individual array element, I am looking to build objects that have width based on the number of matching elements in the array, (each element is 25x25) i.e. if 3 array elements are 1,1,1 then the object will have a width of 75. Maybe some code will help explain:
The following tile array
var arr1 = [
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,2,2,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,0,0,0,0,0,0,0]
];
should produce the following object array:
[
{x: 75, y: 0, width: 100, height: 25, value: 1},
{x: 75, y: 50, width: 50, height: 25, value: 2},
{x: 0, y: 100, width: 75, height: 25, value: 3}
]
but it instead it is producing the following:
[
{x: 75, y: 0, width: 25, height: 25, value: 1},
{x: 100, y: 0, width: 25, height: 25, value: 1},
{x: 125, y: 0, width: 25, height: 25, value: 1}
]
My logic is obviously wrong but I can't for the life of me get it.
Example code is below:
Any help really appreciated:
var tmpObj = {};
var objArr = [];
var arr1 = [
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,2,2,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,0,0,0,0,0,0,0]
];
for (let i=0; i<arr1.length; i++) {
for (let j=0; j<arr1[i].length; j++) {
if (arr1[i][j] > 0 && arr1[i][j] < 6) { // platform blocks only 1 - 5
if (tmpObj.x === undefined) {
tmpObj = {
x: j * 25,
y: i * 25,
width: 25,
height: 25,
value: arr1[i][j]
}
} else if (arr1[i][j] == arr1[i][j-1] && arr1[i][j] == tmpObj.v) {
tmpObj.w += 25;
} else if (arr1[i][j] !== tmpObj.v) { // new tile type
objArr.push(tmpObj);
tmpObj = {
x: j * 25,
y: i * 25,
width: 25,
height: 25,
value: arr1[i][j]
}
} else {
objArr.push(tmpObj);
tmpObj = {};
}
}
}
}
console.log(objArr);
Looking at what you are trying to do your implementation is way too complicated. Rather than hunt down the bug (for which I would have used devTools and stepped through the code line by line to find where the problem was.) I rewrote the function using a while loop to find the width of joined tiles.
I took liberty with the object property names but I am sure you can change it to your needs.
const objArr = [];
const arr1 = [
[0,0,0,1,1,1,1,0,1,0],
[2,0,0,0,0,0,0,0,0,3],
[0,0,0,4,4,0,4,4,4,4],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,5,5,4,0,0,0,0]
];
const tileSize = 25;
for (let i = 0; i < arr1.length; i++) {
const row = arr1[i]
for (let j = 0; j < row.length; j++) {
if (row[j] > 0 && row[j] < 6) {
let count = j + 1;
while (count < row.length && row[count] === row[j]) { count += 1 }
objArr.push({
x: j * tileSize,
y: i * tileSize,
w: tileSize * (count - j),
h: tileSize,
tile: row[j]
});
j = count - 1;
}
}
}
// show results
objArr.forEach(log);
// unrelated to answer. (I hate the console SO has for snippets.)
function log(data){
show.appendChild(
Object.assign(
document.createElement("div"), {
textContent :
Object.keys(data).reduce((str, key) => {
return str + (" " + key+ ": " + data[key]).padEnd(8,".");
}, ""
)
}
)
);
}
<code id="show"></code>
I have an ionic project and am using the following library:
http://gionkunz.github.io/chartist-js/index.html
Actually drawing the chart is achieved with the following:
var chart = new Chartist.Line('.ct-chart', {
series: [
{
name: 'series-1',
data: [
{x: new Date(143134652600), y: 53},
{x: new Date(143234652600), y: 40},
{x: new Date(143340052600), y: 45},
{x: new Date(143366652600), y: 40},
{x: new Date(143410652600), y: 20},
{x: new Date(143508652600), y: 32},
{x: new Date(143569652600), y: 18},
{x: new Date(143579652600), y: 11}
]
},
{
name: 'series-2',
data: [
{x: new Date(143134652600), y: 53},
{x: new Date(143234652600), y: 35},
{x: new Date(143334652600), y: 30},
{x: new Date(143384652600), y: 30},
{x: new Date(143568652600), y: 10}
]
}
]
}, {
axisX: {
type: Chartist.FixedScaleAxis,
divisor: 5,
labelInterpolationFnc: function(value) {
return moment(value).format('MMM D');
}
}
});
With a DIV:
<div class="ct-chart ct-perfect-fourth"></div>
Instead of having a hardcoded array for the series as shown above, I would like to build this dynamically through a function call.
Any example of how I might do that?
Thanks!
You could generate the data with a little randomness and some fixed variables using a generate function. It's probably also nicer to parametize the creation of your chart for easier re-creation later. Chartist also has a update() function that lets you hand it new data and options, so is especially useful for this.
JSFIDDLE
Javascript
var button = document.getElementById('button');
var options = {
axisX: {
type: Chartist.FixedScaleAxis,
divisor: 5,
labelInterpolationFnc: function(value) {
return moment(value).format('MMM D');
}
}
};
var chart; // initialise the chart variable
function createChart(){
chart = new Chartist.Line('.ct-chart', changeData(), options);
}
function updateChart(){
chart.update(changeData());
}
function changeData(){
var series = [];
// set up series ranges
var numberOfSeries = 2;
var numberOfItems = 8;
var startDate = 143134652600;
var endDate = 143579652600;
var minY = 11;
var maxY = 53;
// creates the 'step' each x-axis item should take
var dateDiff = Math.floor((endDate - startDate) / numberOfItems);
// alternatively set the step to a whole day etc. (makes endDate unnecessary)
// var dateDiff = 24 * 60 * 60 * 1000;
for(var x = 0; x < numberOfSeries; x++){
var seriesData = [];
for(var y = 0; y < numberOfItems; y++){
seriesData.push({
x: new Date(startDate + (dateDiff*y)),
y: getRandomInt(minY, maxY)
})
}
series.push({
name: 'series-'+ (x+1),
data: seriesData
});
}
// return the data to display in the chart
return {series:series};
}
/**
* Returns a random integer between min (inclusive) and max (inclusive)
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
button.addEventListener('click', updateChart);
createChart(); // generate chart initially
HTML
<button id="button">
Change Data
</button>
<div class="ct-chart ct-perfect-fourth"></div>
This is a rough example; you could replace the input series1, series2 with an array of arrays and make the lower for-loops two wrapped loops to handle multiple series. This would also entail adding the objects to the series array in the outer loop.
For now, try something like this:
function generateJSON(series1, series2) {
var chartInternal = {
series: [
{
name: 'series-1',
data: []
},
{
name: 'series-2',
data: []
}
]
}, {
axisX: {
type: Chartist.FixedScaleAxis,
divisor: 5,
labelInterpolationFnc: function(value) {
return moment(value).format('MMM D');
}
}
};
for (var i = 0, len = series1.length; i < len; i++) {
chartInternal.series[0].data.push({x: new Date(series1[i].date), y: series1[i].y});
}
for (var i = 0, len = series2.length; i < len; i++) {
chartInternal.series[1].data.push({x: new Date(series2[i].date), y: series2[i].y});
}
return chartInternal;
}
Usage:
var series1 = [
{ date: 1234567, y:52 },
... more
];
var series2 = [
{ date: 7654321, y:52 },
... more
];
var chart = new Chartist.Line('.ct-chart', generateJSON(series1, series2))
I want to create such array in loop
dataset: [
{
x: 0,
y: 0,
},
{
x: 1,
y: 0.993,
}
]
But this way is not correct.
var array = new Array(10);
for (var i = 0; i < 5; ++i) {
array[i].x = 1;
array[i].y = 2;
}
How I can initialize in correct way?
The comments made by SLaks and squint are correct, so this answer is more of an explanation of why your code isn't working like you think it should, and an example of what you could do instead.
You created an array with room to hold 10 things but you didn't specify what those things were and so nothing is contained in the array.
var array = new Array(10);
you can visualize your array like this:
array = [undefined, undefined, undefined, undefined,...
The array you created was just a container for 10 things not yet defined. When you tried to assign the 'x' and 'y' properties of the array elements, you were were trying to operate on something that did not exist. To get what you want, I suggest creating an object that has the properties you want, with initial values, and then use your loop to add the number of elements you want.
var array = [];
var arrayObject = {x:0,y:0};
for(i=0; i < 10; i++){
array.push(arrayObject);
}
You can do this job in one assignment line as follows;
var dataSet = (new Array(10)).fill("initial y value").reduce((p,c,i) => p.concat({x:i,y:c}),[]);
console.log(dataSet);
I just couldn't figure what y values you would like to have so inserted the initial values of the array. Change them the way you like later. I hope it helps.
Replace the new Array(10) with
var array = Array.apply( {}, { length: 10 } ).map( function() { return {} });
new Array(10) is creating an array like
[ undefined, undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined]
So you are trying to assign x on undefined
If you tried
new Array(10).map(function(){ return {}; }) it will not work either.
An es6 way to do it would be
Array.from(new Array(10), () => { return { x: 1, y: 2 }; })
In JavaScript the Array acts different than in static-typed languages, so there's no need to initialize it with fixed length.
For ECMAScript 6 specification and later:
var points = [].fill.call({ length: 5 }, {x: 1, y: 1});
It produces
[{x: 1, y: 1},
{x: 1, y: 1},
{x: 1, y: 1},
{x: 1, y: 1},
{x: 1, y: 1}]
To ensure old browsers' support use for loop:
var points = [{x: 1, y: 1}];
for (var i = 0; i < 5; i++) points.push(points[0]);
I want to make an array within an array.
It would contain some enemies with all different positions. I tried writing it like this:
var enemies = [
position = {
x: 0,
y: 0
}
];
enemies.length = 6;
This seem to work, however, I don’t know what to write when I want to call it. enemies[1].position.x doesn’t work at all. Any leads?
You probably want an array of enemy objects instead. Something like:
var enemies = [
{ position: { x: 0, y: 0 } },
{ position: { x: 0, y: 0 } },
{ position: { x: 0, y: 0 } }
];
var firstEnemy = enemies[0];
firstEnemy.position.x; // 0
var totalEnemies = enemies.length; // 3
What you want to do is
var enemies = [
{
position: { x: 0, y: 0 }
}
];
enemies[0].position.x;
Arrays index starts from 0
Also, let's deconstruct why your code doesn't throw an error, and what it exactly does.
You are declaring an array named enemies, and when declaring it you are defining a global variable named position (and its value is returned)
After execution, enemies look like this [ {x: 0, y: 0} ]
How to obtain value from JSON element being created dynamically. Example below. I was wondering if there is way to get the value from previous element 'top' using some javascript. (Please update the title if its incorrect or misleading)
Following this example on jsfiddle
var info = [
{
src: "http://dummyimage.com/55x80/0d0.png/fff",
pos: {
top: 93,
left: 70
},
rotate: -10,
zIndex: 3
},
{
src: "http://dummyimage.com/55x80/d00.png/fff",
pos: {
top: previousElement(top) + some dynamic value added at run time,
left: 70
},
rotate: 0,
zIndex: 2
},
]
I wouldn't do this, you can calculate it as you go based on the index.
But... if you insist:
Assuming your array is a JS literal (and not JSON, which is different) you can use a simple counter.
Using the fact assignment returns the assigned value (this is kind of ugly though):
var top = 93;
var info = [
{
src: "http://dummyimage.com/55x80/0d0.png/fff",
pos: {
top: top,
left: 70
},
rotate: -10,
zIndex: 3
},
{
src: "http://dummyimage.com/55x80/d00.png/fff",
pos: {
top: top = top + (some dynamic value added at run time),
left: 70
},
rotate: 0,
zIndex: 2
} //... keep doing top = top + sometDynamicValue on all elements
]
A more isolated example might be:
var c = 0,
arr = [
{a:c = c + 5},
{a:c = c + 5},
{a:c = c + 5},
{a:c = c + 5},
{a:c = c + 5},
]
Which creates an array of objects with an increasing by 5 a property.
I was wondering if there is way to get the value from previous element 'top' using some javascript
info is an array so all you need to do is index into it then access the prop object i.e.
var prevTop = info[someIndex].pos.top;
There's no simple syntax to get the value from the previous element in an array literal in Javascript. You have to reference it by index, like this:
pos: {
top: info[0].pos.top + value
left: 70
},
Try this function:
function DoOffset(data, offset)
{
for (var i = 1; i < data.length; i++)
{
data[i].pos.top = data[i - 1].pos.top + offset;
}
}
You can loop through the info array to change the value:
for(var i = 1, len = info.length; i < len; i++) {
info[i].pos.top = info[i - 1].pos.top + newValue;
}