function genEnemy(a) {
//javascript:alert(en[0]+'\n'+genEnemy(en[0])+'\n'+en[0])
with (Math) {
a[1]=round(a[1]*(.5+random()))
a[2]=round(a[2]*(1+random()))
for (var b=0;b<5;b++) a[3][b]=round(a[3][b]*(a[3][b]/2+random()*a[3][b]/10))
for (var b=0;b<a[4].length;b++) random()<it[a[4][b]][3]/10?a[4][b]=0:0
}
return a
}
Script to generate an enemy's stats given the bases each enemy array. (RPG game)
The problem is, when I am expecting it to return an array containing the new stats, it also sets the enemy array to the new one. Why is this? Obviously you can see how problems are caused by this (the bases being changed so a weak enemy can become uber powerful). How would I stop it from setting the array in en (array of enemy values)?
Objects are passed by reference in JavaScript. That means any changes you make to the array a inside genEnemy is reflected on the original array that was passed in. You need to make a deep copy of the array and return this copy. Here is a function that will do it for you:
function cloneArray(a) {
var b = [];
for (var i = 0; i < a.length; i++)
if (a[i] instanceof Array)
b[i] = cloneArray(a[i]);
else
b[i] = a[i];
return b;
}
Inside genEnemy, you would then do:
a = cloneArray(a);
// make changes to the new array
return a;
Also, don't forget to include semicolons in your code. Even though they are optional, you may run into unexpected problems if you get into the habit of omitting them.
Are you passing array en to your getEnemy() function? If so then it will change the values in en array as it's passed by reference not by value
Related
Essentially my I am trying to initialize a JavaScript object and have it contain empty objects with a single key. For example:
getOject('one.two.three')
Would result in the object:
{one:{two:{three:''}}}
As far as I can tell, you can't initialize with dynamic key names unless you use array notation
root[dynamicKey] = 'some variable';
so I need to loop through and based on the number of args initialize each one then assign it's value but the syntax doesn't seem to let me do this in any way that I know of.
So, if it were not a loop it would be like this:
jsonifiedForm[rootKey] = {};
jsonifiedForm[rootKey][childKeys[0]] = {};
jsonifiedForm[rootKey][childKeys[0]][childKeys[1]] = $input.val();
I can't think of a way to do this, I am not typically a JS guy so it might be something simple but I couldn't find anything on Google or Stack Overflow
Thank you in advance!
This function should be what you're looking for.
function getOject(str) {
// this turns the string into an array = 'one.two.three' becomes ['one', 'two', 'three']
var arr = str.split('.');
// this will be our final object
var obj = {};
// this is the current level of the object - in the first iteration we will add the "one" object here
var curobj = obj;
var i = 0;
// we loop until the next-to-last element because we want the last element ("three") to contain an empty string instead of an empty object
while (i < (arr.length-1)) {
// add a new level to the object and set the curobj to the new level
curobj[arr[i]] = {};
curobj = curobj[arr[i++]];
}
// finally, we append the empty string to the final object
curobj[arr[i]] = '';
return obj;
}
Because JavaScript references values in variables instead of copying them "into" variables, we can make our initial value, then make a reference to it which we'll move around as we delve down in:
var getOject = function (k, s) {
// initialize our value for return
var o = {},
// get a reference to that object
r = o,
i;
// we'll allow for a string or an array to be passed as keys,
//and an optional sepeartor which we'll default to `.` if not given
if (typeof k === 'string') {
k = k.split(s || '.');
}
// do we have an array now?
if (k && k.length) {
//iterate it
for (i = 0; i < k.length; i += 1) {
// set a property on the referenced object
r[k[i]] = {};
// point the reference to the new level
r = r[k[i]];
}
}
// send back the object
return o;
}
console.log(getOject('one.two.three'));
console.log(getOject('four|five|six', '|'));
r points to the same thing that o does, initially, and as we move the reference (r) deeper into o and write to it, we're building out o as we go.
The two console.log() calls at the end output the following:
Also notice I let you pass in an array to start with if you feel like it, and made the separator a parameter so that you're not stuck with .
I am wondering, what is the best approach to write a recursive function with no direct base case (say: factorial), for instance, to count the number of elements in a nested array I have two approaches in mind, the first one below is preferred as it returns result directly:
the second one keeps the count in a variable attached to the function, works fine, but dealing with the result & resetting the variable is bizarre.
any pointers are appreciated.
You can simply return the value you are interested in:
function countElements(arr) {
var count = 0;
for (var i=0; i<arr.length; i++) {
if (arr[i] instanceof Array) {
count += countElements(arr[i]); // recursion here
} else {
count++; // normal element counts as 1
}
}
return count;
}
Demo: http://jsbin.com/ejEmOwEQ/1/edit
WARNING: The function might not end if the array contains self reference (var arr = []; arr.push(arr); countElements(arr);)
The correct way to write this is simply:
function countElements (obj) {
if (obj instanceof Array) {
var count = 0;
for (var i in obj)
count += countElements(obj[i]);
return count;
}
return 1
}
The terminating condition you're looking for is if not instanceof Array. Which in my code above is simply the fall through from the if instanceof Array block.
You do not need to keep a temp variable like count in recursive functions. You're still thinking iteratively (well, that for loop is iterative so you need a count variable there).
Recursive functions do everything by accepting arguments and returning results. No assignments are necessary. In fact, the code above can be written purely recursively without using a for loop and therefore without needing to use a count variable:
function countElements (obj) {
if (obj instanceof Array) {
if (obj.length) {
return countElements(obj.shift()) + countElements(obj);
}
return 0;
}
return 1;
}
There are 3 rules: if object is not an array we return 1, if object is an empty array we return 0 otherwise we count the first item in the array + the sum of the rest of the array.
I was pondering something earlier. I wanted to check if a function had already been put into an array. If so throw an exception. I did this little test with the console...
So I guess I could say that the objects are always just references, and after setting a as x I could change x and a would be effected as well?
Would this mean that the condition x = a no matter what, which is what I want.
Using this to check if the function/object is already in the array I could just do this correct...
Is there a better way to do this?
Would this also mean that if I pass a variable to a function and mutate it in that function it will be mutated outside of that function as well?
EDIT
I guess I am right about the mutation with this little test. But I don't get why its bar in the first log in the second example
EDIT END
Example 1:
var x = function(){console.log("hello")}; var a = function(){console.log("hello")};
console.log(x == a); //comes out false
//Set a as x
a = x;
console.log(x == a); //comes out true
Example 2:
Array.prototype.Contains = Array.prototype.Contains || function (obj) {
return this.indexOf(obj) != -1;
};
var x = function(){console.log("hello")}; var a = function(){console.log("hello")};
var z = a;
console.log(x == a); //comes out false
var l = [];
l.push(x);
//Set a as x
a = x;
l.push(a);
console.log(x == a); //comes out true
console.log(l.Contains(x)); //Should come out true
console.log(l.Contains(a)); //Should come out true
console.log(l.Contains(z)); //Should come out false
Your question isn't entirely clear to me but I'll try to answer as best I can.
Improving the function
Your function could be simplified to leverage the indexOf function.
Array.prototype.Contains = Array.prototype.Contains || function (obj) {
return this.indexOf(obj) >= 0;
};
Also I want to point out that in your implementation you're looping through everything when you could exit early by returning inside the if.
Array.prototype.Contains = Array.prototype.Contains || function (obj) {
var i;
for (i = 0; i < this.length; i += 1) {
if (this[i] === obj) {
return true;
}
}
return false;
};
x == a
I think you understand but just want to clarify, x and a are different originally because they are referencing different functions. When you set x = a they are both pointing at the function declared originally in x and are therefore the same. Even thought the functions are identical in terms of implementation, they were both constructed and then placed in different parts of memory.
When you do this:
var x = function(){console.log("hello")}; var a = function(){console.log("hello")}
x and a point to different functions. Even if you compare them for equality, they are not equal as all equality checking does here is see if they point to the same function or not - there is no attempt made to see if they would produce the same output when run or not (that is almost impossible to do in general, after all).
When you do something like x = a, x now references whatever a is referencing - the same object. So now they compare equal.
If you need to see if a function already exists in an array, I suggest instead of just placing arrays in a big list, you create a dictionary (hashmap, hashtable, whatever you want to call it) that uses strings as keys as function as values. The key would be the 'name' of a function - whenever you make that function you'd use the same name, and names in different places in memory but with the same characters in them WILL compare equal.
You're really confused. Everything in JavaScript (except for primitive data types, null and undefined) is an object, and objects are stored in variables as reference. Read the following answer to know more about the differences between the two: https://stackoverflow.com/a/13268731/783743
When you define two identical functions (in your case x and a) JavaScript sees them as separate functions. This is because in addition to the function body a function also maintains its own environment and state. Hence x and a are not the same function which is why x === a returns false.
By setting x = a you're essentially copying the reference stored in a into x. Hence they now point to the same function (which is the function a was originally referring to). The function x was originally referring to is now lost and will eventually be garbage collected. Thus x === a now returns true.
BTW you don't need to create a special Contains function to check whether an object is already inside an array. Just use indexOf:
var array = [];
function x() {}
array.indexOf(x); // returns -1
array.push(x);
array.indexOf(x); // returns 0
If the index is less than 0 the object is not in the array.
If you want to check whether the function body of two functions is the same then use this function:
function sameFunctionBody(a, b) {
return String(a) === String(b);
}
Now console.log(sameFunctionBody(x, a)) will return true as long as both the functions are exactly the same (including whitespace, parameters, function name, etc).
I am trying to remove an element from a Javascript associtive array using the value to find it, but I am having trouble. I have tried splice and JQuery's grep method and neither have worked for me. This is what I currently have.
var array_path = new Array();
function bulk_upload(){
var temp_array = new Object();
for (var i = 1; i<8; i++){
temp_array[i] = $('#path' + i).val();
if(temp_array[i]!='' && temp_array[i]!=null){
array_path['path' + i] = $('#path' + i).val();
}
}
process_txt();
}
function process_txt(){
//alert(array_path.indexOf(full_path)); //returns nothing
var removed_element = array_path.splice(getKey(array_path), 1);
//array_path = $.grep(array_path, function(val) { return val != full_path; });
alert(removed_element);//return nothing, just blank alert box
}
function getKey(data) {
for (var prop in data)
return prop;
}
The way to do this is to use the delete operator.
delete array_path[getKey(array_path)]
Some Background Information
In JavaScript, almost everything descends from Object.prototype. JavaScript, being an open and dynamic language allows you to create/modify properties of objects by simple assignment. This is very similar to what an associative array -- a structure that contains keyed values.
Under the hood an array is just an object that descends from Array.prototype with numeric keys and a special property called length. The length property just returns one greater than the highest numeric property. In essence, an Array is an object with different semantics.
If you're wanting an associative array then Array is not the object you want to descend from. You would want to descend directly from Object. There are two ways to do that, you could either use the new operator or an empty object literal. The syntax for both is below:
var o = new Object();
var o = {};
The second is preferred since it's a little bit more concise.
I wrote a blog post about this a while back, have a look if you want a little bit more info.
There is no such thing in JavaScript as an "associative array" per se. The data structure which corresponds to this concept is simply a JavaScript Object.
Of course, a JavaScript Array (like essentially everything in JavaScript) is an Object, but one with additional capabilities. So you can use an Array as a key-value map, but it's really not the correct structure for that.
To remove a key from an Object, you just do something like this:
var myObj = {};
var myKey = "blah";
myObj[myKey] = 1234; // Adds or updates value for "blah" to 1234.
delete myObj[myKey]; // Removes key-value pair for "blah".
Have you tried delete hash.someKey; ?
You can give your object a remove method, or use apply or call to use another object's remove method, if defined.
function myObj(members){
for(var p in members) this[p]= members[p];
}
myObj.prototype.remove= function(val){
for(var p in this){
if(this[p]=== val) delete this[p];
}
return this;
}
myObj.prototype.toString= function(){
var A= [];;
for(var p in this){
if(this.hasOwnProperty(p)){
A.push(p+':'+this[p])
}
}
return '{'+A.join(', ')+'}';
}
var O= new myObj({a: 1, b: 10, c: 100});
alert(O)
O.remove(10);
alert(O)
I'm not psychic, so I can only guess that you wanted to accomplish something like this:
var paths = [];
function getPaths() {
for(var i = 1; i < 8; ++i) {
var value = $('#path' + i).val();
if(value) paths.push(value);
}
}
function process() {
var firstPath = paths.shift();
// do stuff
}
getPaths();
if(paths.length) process();
This is so simple I am baffled. I have the following:
var x = 'shrimp';
var stypes = new Array('shrimp', 'crabs', 'oysters', 'fin_fish', 'crawfish', 'alligator');
for (t in stypes) {
if (stypes[t] != x) {
alert(stypes[t]);
}
}
Once the values have iterated it starts returning a dozen functions like
function (iterator, context) {
var index = 0;
iterator = iterator.bind(context);
try {
this._each(function (value) {iterator(value, index++);});
} catch (e) {
if (e != $break) {
throw e;
}
}
return this;
}
What the heck is going on?
Edit: In these scripts I am using http://script.aculo.us/prototype.js and http://script.aculo.us/scriptaculous.js I remember now reading about the way prototype extends arrays and I am betting this is part of it. How do I deal with it?
The for enumeration is going to go over every member of the object you passed it. In this case an array, which happens to have functions as members as well as the elements passed.
You could re-write your for loop to check if typeof stypes[t] == "function" or yada yada. But IMO you are better off just modifying your looping to only elements..
for(var i = 0, t; t = stypes[i]; ++i){
if (t != x) {
alert(t);
}
}
Or
for(var i = 0; i < stypes.length; ++i){
if (stypes[i] != x) {
alert(stypes[i]);
}
}
I wanted to migrate my last comment up to the answer to add the notice of the a caveat for the first type of loop.
from Simon Willison's "A re-introduction to JavaScript"..
for (var i = 0, item; item = a[i]; i++) {
// Do something with item
}
Here we are setting up two variables.
The assignment in the middle part of
the for loop is also tested for
truthfulness - if it succeeds, the
loop continues. Since i is incremented
each time, items from the array will
be assigned to item in sequential
order. The loop stops when a "falsy"
item is found (such as undefined).
Note that this trick should only be
used for arrays which you know do not
contain "falsy" values (arrays of
objects or DOM nodes for example). If
you are iterating over numeric data
that might include a 0 or string data
that might include the empty string
you should use the i, j idiom instead.
you want to do:
for (var i in object) {
if (!object.hasOwnProperty(i))
continue;
... do stuff ...
}
As for..in enumeration iterates over all properties (enumerable or otherwise) that exist on both the object and its prototype chain. The hasOwnProperty check restricts iteration to just those properties on the actual object you want to enumerate.
ES5 makes things a little better for library developers (and help avoid this stuff) but we won't see that ina shipping browser for quite a while :-(
[edit: replacing return with continue. lalalalala ;) ]
Since prototype has extended the array for your convenience you should take advantage of it. Your example could be rewritten as:
var x = 'shrimp';
var stypes = new Array('shrimp', 'crabs', 'oysters', 'fin_fish', 'crawfish', 'alligator');
stypes.without(x).each(alert);
It should be
for (t in stypes) {
if (t != x) {
alert(t);
}
}