Racking my brains on this one. I have the code below: the first stages of a JavaScript game. All the objects are well-defined and I'm using jQuery for DOM interaction. The puzzle is created with the following JS code:
var mypuzzle = new puzzle("{solution:'5+6+89',equations:[['5+3=8',23,23],['5+1=6',150,23],['5+3=6',230,23]]}");
However, the loop at the bottom of the code won't go further than the first iteration. Any idea why? No errors are thrown at all.
function equationBox(equation, top, left) {//draggable equation box
this.reposition = function() {
this.top = 0;
this.left = 0;
}
this.top = 0;//make random
this.left = 0;//make random
this.equation = equation;
if(top && left) {
this.top = top;
this.left = left;
}
this.content = this.equation.LHS.string + '<span> = </span>' + this.equation.RHS.string;
this.DOM = $('<li>').html(this.content);
}
function puzzle(json) {
this.addEquationBox = function(equationBox) {
$('#puzzle #equations').append(equationBox.DOM);
}
this.init = function() {
//this.drawPuzzleBox();
this.json = JSON.parse(json);
this.solution = new expression(this.json.solution || '');
this.equations = this.json.equations || [];
var iterations = this.equations.length;
for(i=0;i<iterations;i++)
{
console.log(i);
this.addEquationBox(new equationBox(stringToEquation(this.equations[i][0]),this.equations[i][1], this.equations[i][2]));
}
}
this.init();
}
Possibly your failure to scope your counter variable is doing it, especially if you make a habit of it (since you're using the global variable of that name, and any loops you wrote in any code you're calling may be doing the same thing). Try:
for(var i=0;i<iterations;i++)
because this.equations = this.json.equations || [] , and, since this.json.equations is undefined, it get assigned to []
Assuming you're using JSON.parse as defined at https://github.com/douglascrockford/JSON-js/blob/master/json2.js, it appears that your json string is not parsing properly:
var string1 = "{solution:'5+6+89',equations:[['5+3=8',23,23],['5+1=6',150,23],['5+3=6',230,23]]}"
JSON.parse(string1); // throws SyntaxError("JSON.parse")
When I use JSON.stringify, defined in the same file, to create a JSON string from your object:
var obj = {solution:'5+6+89',equations:[['5+3=8',23,23],['5+1=6',150,23],['5+3=6',230,23]]}
var string2 = JSON.stringify(obj);
// {"solution":"5+6+89","equations":[["5+3=8",23,23],["5+1=6",150,23],["5+3=6",230,23]]}
JSON.parse(string2); // returns a proper object
Note that the string that JSON.stringify is creating is different than the one you are trying to use, which might be the cause of your problem.
Related
So this is what I am dealing with:
At the top of the js file i declare these variables:
let teensArray = [];
let copyteensArray = [];
let teensArrayLength = 0;
let teensArrayPlayed = 0;
$.getJSON("json/teens.json", function(data) {
teensArray = data.statements;
copyteensArray = data.statements;
teensArrayLength = data.statements.length;
$(".teens .total-cards").text(teensArrayLength + " CARDS")
});
Everytime the mode is "teens" this executes:
if (mode == "teens") {
let currStatement = copyteensArray[getRandomInt(copyteensArray.length)]
let index = copyteensArray.indexOf(currStatement)
copyteensArray.splice(index, 1);
currentPack = "TEENS PACK";
currentColor = "#23B574";
srcImg = "svg/007-party hat.svg"
playedCardsText = ++teensArrayPlayed + " / " + teensArrayLength;
console.log(copyteensArray.length);
console.log(teensArray.length);
return currStatement;
}
The problem: The teensArray has the same values as copyteensArray after the if statement.
Example values:
Before if statement
teensArray["1","2","3","4"]
copyteensArray["1","2","3","4"]
Inside if statement
copyteensArray.splice(index, 1);
After the return inside the if statement
teensArray["1","3","4"]
copyteensArray["1","3","4"]
This function executes ONLY if the user decides to go back to another screen:
function reset() {
copyteensArray = teensArray;
teensArrayPlayed = 0;
}
Am I missing something?
copyteensArray = teensArray; makes the same pointer point to both of them. Consider using copyteensArray = [...teensArray]; which assigns brand new instance of teensArray content to copyteensArray
You have a "shallow" copy of the array, ie both the array point to the same objects. Any changes made to the shallow copy will change the original object.
You can visualize it as a shortcut in Windows. Whatever change you make to the shortcut also changes the original object.
Solve this making a hard copy. There are multiple ways in ES6 to do that.
hardCopy = [...OriginalArray];
And another way is to JSON.stringify() the object and parse it back.
hardCopy = JSON.parse(JSON.stringify(OriginalArray));
For your code, fix this by changing these lines
$.getJSON("json/teens.json", function(data) {
teensArray = [...data.statements];
copyteensArray = [...data.statements];
I am currently building a website like PCPartPicker but for watercooling parts for a school project. I dove in and I am having some issues. The most important on being this:
Here is my object constructor to start
var cpuCollection = [];
var myComputer = [];
function CPU(frequency,cores,socket,name) {
this.name = name;
this.frequency = frequency;
this.cores = cores;
this.socket = socket;
cpuCollection.push(this);
}
var i75930k = new CPU(3.6, 6, 2011.3, "i7 5930k");
var i54690k = new CPU(3.6, 4, 1150, "i5 4960k");`
After I built the object constructor I made some test objects using real computer parts.
In my HTML I have drop down menus that are populated by the objects on load using this code:
$(cpuCollection).each(function() {
$('#cpusel').append($("<option> " + this.name + "</option>"))
});
From there I wanted to make it so that when an option was selected in the dropdown the proper object would be pushed into the myCPU var for compatibility testing in another function. The code I used to accomplish this is as follows:
$('#cpusel').change(function() {
myCPU = new CPU();
$(cpuCollection).each(function(){
if(this.name = $('#cpusel :selected').text()) {
myCPU = this;
}
});
});
Unfortunately this code currently isn't working and is telling me that myCPU.socket is 1150 when the i75930k is selected when it really should be 2011.3. I am getting to wits end here and want to make some progress.
Thanks for the help in advance.
Edit: I fixed the equals sign issue and now I am thinking that the problem may be stemming from the way I push the objects into the cpuCollection array. When I try and log cpuCollection I get [CPU, CPU] which is obviously not what I want. How can I push the CPU objects on creation into cpuCollection with all of their properties intact.
Try this in your if() statement:
$('#cpusel').change(function() {
myCPU = new CPU();
$(cpuCollection).each(function(){
if(this.name === $('#cpusel :selected').text()) {
myCPU = this;
}
});
So, there were a few issues with some of your logic/syntax, and I will try to address them individually so you can better understand how I got a working solution:
1) You are pushing the element to an array from inside your object definition. This is typically bad practice, as it is not reusable, and will throw an error if an array called cpuCollection is not defined prior to the instantiation of that instance of the CPU object. It is better to say cpuCollection.push(new CPU(...));
2) When you append the options to the dropdown list, you should add the value property as well, so you can more easily grab the value in #3
3) If you set the value propery on the <option> elements, there is no need to look for the text of the selected option, you can simplify your selector to $('#cpusel').val()
4) There is no need to wrap your arrays in a jQuery object by saying $(cpuCollection).each(...), you can (and should) use the built-in vanilla javascript array operations.
5) I changed your .each() in the change handler to be a .some() this is because when you return true from .some() it stops any further iteration, which is what you want. Previously, it would continue to loop to the end, even if it already found the matching CPU.
6) I added a myCPU variable and instantiated it to null, so if you are running in strict mode, your change handler wouldn't throw an error for the variable not having been previously defined.
7) Your logic in your if statement was doing an assignment, rather than a comparison because you used = instead of == or ===, simple mistake.
Hope this helps!
var cpuCollection = [];
var myComputer = [];
var myCPU = null;
function CPU(frequency,cores,socket,name) {
this.name = name;
this.frequency = frequency;
this.cores = cores;
this.socket = socket;
}
cpuCollection.push(new CPU(3.6, 6, 2011.3, "i7 5930k"));
cpuCollection.push(new CPU(3.6, 4, 1150, "i5 4960k"));
cpuCollection.forEach(function(cpu, index) {
$('#cpusel').append($('<option value="'+ cpu.name + '">' + cpu.name + '</option>'))
});
$('#cpusel').change(function() {
myCPU = new CPU();
cpuCollection.some(function(cpu, index){
if(cpu.name === $('#cpusel').val()) {
myCPU = cpu;
return true;
}
});
console.log(myCPU);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="cpusel"></select>
There is simple way(s) :
var cpuCollection = [];
var myComputer = [];
function CPU(frequency,cores,socket,name) {
cpuCollection.push({
name: name,
frequency: frequency,
cores: cores,
socket: socket
});
}
var i75930k = CPU(3.6, 6, 2011.3, "i7 5930k");
var i54690k = CPU(3.6, 4, 1150, "i5 4960k");
$(cpuCollection).each(function(i,cpu) {
var option = $('<option/>').html(cpu.name).data('cpu',cpu);
$('#cpusel').append(option);
});
$('#cpusel').change(function() {
var selected = $(this).find('option:selected');
var text = $(selected).text();
var myCPU;
// You can simply use : myCPU = $(selected).data("cpu")
console.log('via data:',$(selected).data("cpu"));
$(cpuCollection).each(function(i,cpu){
if($.trim(cpu.name) == $.trim(text)) {
myCPU = cpu;
// return false; ==> break the each();
}
});
console.log('via each:',myCPU);
});
See this demo.
I need a way to add an object into another object. Normally this is quite simple with just
obj[property] = {'name': bob, 'height': tall}
however the object in question is nested so the following would be required:
obj[prop1][prop2] = {'name': bob, 'height': tall}
The clincher though, is that the nesting is variable. That is that I don't know how deeply each new object will be nested before runtime.
Basically I will be generating a string that represents an object path like
"object.secondObj.thirdObj.fourthObj"
and then I need to set data inside the fourth object, but I can't use the bracket [] method because I don't know how many brackets are required beforehand. Is there a way to do this?
I am using jQuery as well, if that's necessary.
Sure, you can either use recursion, or simple iteration. I like recursion better. The following examples are meant to be proof-of-concept, and probably shouldn't be used in production.
var setDeepValue = function(obj, path, value) {
if (path.indexOf('.') === -1) {
obj[path] = value;
return;
}
var dotIndex = path.indexOf('.');
obj = obj[path.substr(0, dotIndex)];
return setDeepValue(obj, path.substr(dotIndex + 1), value);
};
But recursion isn't necessary, because in JavaScript you can just change references.
var objPath = 'secondObj.thirdobj.fourthObj';
var valueToAdd = 'woot';
var topLevelObj = {};
var attributes = objPath.split('.');
var curObj = topLevelObj;
for (var i = 0; i < attributes.length; i++) {
var attr = attributes[i];
if (typeof curObj[attr] === 'undefined') {
curObj[attr] = {};
}
curObj = curObj[attr];
if (i === (attributes.length - 1)) {
// We're at the end - set the value!
curObj['awesomeAttribute'] = valueToAdd;
}
}
Instead of generating a string...
var o="object";
//code
o+=".secondObj";
//code
o+=".thirdObj";
//code
o+=".fourthObj";
...you could do
var o=object;
//code
o=o.secondObj;
//code
o=o.thirdObj;
//code
o=o.fourthObj;
Then you can add data like this:
o.myprop='myvalue';
And object will be updated with the changes.
See it here: http://jsfiddle.net/rFuyG/
This question already has an answer here:
when i run my code I get the following result []object object] [object object] but should be giving me an ordered array
(1 answer)
Closed 8 years ago.
below is the code that i used to create a range object within my overall programme
function parseRangeString(id, range) {
var myRangeString = range;
var myRangeStringArray = myRangeString.split(/[\s]+/);
var myMax;
var myMin;
var myMinOp;
var myMaxOp;
var myMaxInc = false;
var myMinInc = false;
var op1;
var op2;
var cons1;
var cons2;
op1 = myRangeStringArray[0];
cons1 = parseFloat(myRangeStringArray[1]);
if (myRangeStringArray[2] != null) {
op2 = myRangeStringArray[3];
cons2 = parseFloat(myRangeStringArray[4]);
}
if (cons1 < cons2) {
myMin = cons1;
myMinOp = op1;
myMax = cons2;
myMaxOp = op2;
} else {
myMin = cons2;
myMinOp = op2;
myMax = cons1;
myMaxop = op1;
}
if (myMaxOp.indexOf('=') != -1) {
myMaxInc = true;
}
if (myMinOp.indexOf('=') != -1) {
myMinInc = true;
}
firstRange = new Range(id, myMin, myMax, myMinInc, myMaxInc); //gives back a range object
return firstRange;
}
Now i need to make another function that converts the range object to string, help needed asap because i am stuck atm!
You can overwrite the standard toString function on your javascript objects to make them return whatever you want. Consider this example (demo):
var a = { some_property:'this could be coming from the user' }; // create a new object
a.toString = function(){
// in here, the "this" will point to the object in "a" variable. (well, at least mot of the times)
return this.some_property;
};
console.log(''+a); // force the object to string
If you create a lots of object like this, consider using the prototype of them to place the toString function, will be more efficient, MDN has great examples.
Well i guess i give the same answer every day. :)
JSON.stringify( range );
Hi i have build my own text-wrap class. and i've run into a small problem it is kind off slow, because my script that checks the size of the font puts the string into a div with the classes but thats on a big scale intens for the DOM. so is there another way?
as you can see i tryed to build a cache controller but that makes it slower
var textMetrics = function (appendTo) {
var span;
var cache = [];
this.init = function () {
span = document.createElement("span");
appendTo.appendChild(span);
span.style.position = 'absolute';
span.style.left = -9999 + 'px';
};
this.checkCache = function (word, style) {
for (var i = 0; i < cache.length; i++) {
if (cache[i].word == word) {
return cache[i].value;
}
}
return false;
};
this.addCache = function (word, style, value) {
cache.push({
"word": word,
"style": style,
"value": value
});
};
this.getSize = function (word, style) {
word = word.replaceAll(" ", " ");
//var inCache = this.checkCache(word, style);
var inCache = false;
if (inCache === false) {
span.innerHTML = word;
for (var i in style) {
span.style[i] = style[i];
}
var coords = {
"width": span.offsetWidth,
"height": span.offsetHeight
};
for (var i in style) {
span.style[i] = "";
}
span.innerHTML = "";
this.addCache(word, style, coords);
return coords;
}
else {
return inCache;
}
};
this.init();
};
You could consider making your cache a dictionary (JS object) instead of a list:
var cache = {};
this.addCache = function (word, style, value) {
cache[word] = value;
};
this.checkCache = function (word, style) {
var value = cache[word];
if (typeof value != "undefined")
return value;
return false;
};
I didn't really get what your style variable is about — maybe you should add it to the cache key as well.
Since you are basically using a dictionary the best format for your cache is a simple javascript object that behaves as a hashmap.
var cache = {};
You can then assign words to it as follows:
this.addCache = function (word, style, value) {
cache[word] = {style: style, value: value};
};
And check them as follows:
this.checkCache = function (word) {
return cache[word];
};
So then you can:
var cachedItem = this.checkCache(word);
if (cachedItem ) {
alert(cachedItem.value);
}
This should speed up your cache searches considerably as you dont have to loop through an array that keeps getting larger and larger.
You could try to approximate the text width using the widths of individual characters. This will introduce problems when there's special kerning for combinations like "ff", of course.
I wrote a function that caches the widths of pairs of characters to accommodate for that. Thus, only a constant number of DOM manipulations is needed. It's on http://jsfiddle.net/wbL9Q/6/ and https://gist.github.com/1562233 (too much code for here).
However, while this worked for me in Safari (Mac), it did not give the correct results in Firefox. Apparently, some even more complex kerning is applied there. Maybe extending the algorithm to triples of characters (or even more) could help.
(Sorry for posting my 2nd answer, but I thought it makes sense because it's a completely different aspect.)