javascript, for loop defines a dynamic variable name - javascript

Folks,
Not sure if eval() or window() is the answer, but I am trying to loop through an array, and create variables for each item in that array dynamically.
My myArray looks like:
['foo','bar','baz']
code:
for (var i = myArray.length - 1; i >= 0; i--) {
var myVar = eval(myArray[i])
};
console.log(foo)
console.log(bar)
console.log(baz)
Is this possible?

I think you could do it by creating parameters in an object maybe?
var myObject = {};
for(var i=0;i<myArray.length;i++) {
myObject[ myArray[i] ];
}
If you don't set them to anything, you'll just have an object with some parameters that are undefined. I'd have to write this myself to be sure though.

You cannot create different "variable names" but you can create different object properties. There are many ways to do whatever it is you're actually trying to accomplish. In your case I would just do
for (var i = myArray.length - 1; i >= 0; i--) {
console.log(eval(myArray[i]));
};
More generally you can create object properties dynamically, which is the type of flexibility you're thinking of.
var result = {};
for (var i = myArray.length - 1; i >= 0; i--) {
result[myArray[i]] = eval(myArray[i]);
};
I'm being a little handwavey since I don't actually understand language theory, but in pure Javascript (including Node) references (i.e. variable names) are happening at a higher level than at runtime. More like at the call stack; you certainly can't manufacture them in your code like you produce objects or arrays. Browsers do actually let you do this anyway though it's terrible practice, via
window['myVarName'] = 'namingCollisionsAreFun';
(per comment)

Related

Why is there an extra variable in this javascript function?

I'm a javascript newbie, and I can't figure out why the variable o exists in the following code:
var data = [ {"name":"alice", "group":1}, {"name":"bob", "group":2} ];
function getGroup(n) { return n.group; }
function network(nodes, index) {
var gc = {};
nodes.forEach(function(n) {
var i = index(n), o;
if (condition) {
o = gc[i] || (gc[i] = {x:0,y:0,count:0});
o.x += n.x;
o.y += n.y;
o.count += 1;
}
});
}
var net = network(nodes, getGroup)
It seems to me that the iterator in the network function would be better written this way:
function network(data, index) {
var gc = {};
nodes.forEach(function(n) {
var i = index(n);
if (condition) {
gc[i] = gc[i] || (gc[i] = {x:0,y:0,count:0});
gc[i].x += n.x;
gc[i].y += n.y;
gc[i].count += 1;
}
});
}
I think the second loop is equivalent and it eliminates a variable (and it's easier for me to read). Is the first version following some recommended practice in javascript? I can imagine that it would be good practice to have var o; inside the iterator to reduce its scope, but in this case o only refers to an object that's already referenced by gc[i]. Why not just eliminate it?
Likely because g[i] requires indexing into an array repeatedly looking up a property of an object, whereas storing the value in o caches the value once and provides for faster access subsequently.
This is a (minor) performance enhancement. It replaces three subscripting operations with a local variable assignment and three local variable accesses. I have my doubts whether the performance improvement would be noticeable. See this thread for more info on the performance of various array operations. (Actually, that info is somewhat irrelevant because gc is an object, not an array. These are plain old property look-ups being saved, not array indexing, so the performance increase might be a little more significant.)
Even simpler
change
gc[i] = gc[i] || (gc[i] = {x:0,y:0,count:0});
to
gc[i] = gc[i] || {x:0,y:0,count:0};
Because it has to keep re-seeking the item in the array via the index. o is storing the reference to the location in memory so it's quicker to access later.

Why is result different (using var vs. let)?

This uses var
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 10
This uses let
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 6
I don't understand why the result is different. Can somebody guide me?
The resulting array consists of functions, each function body looks like this:
console.log(i);
The value of i depends on whether we used var or let to declare the variable.
var (ECMAScript 5 and 6)
Here i is a global variable whose value is 10 after exiting the loop. This is the value that is logged.
let (ECMAScript 6)
Here i is a local variable whose scope is restricted to the for statement. Moreover, this variable gets a fresh binding on each iteration. This is best explained by your code transpiled to ECMAScript 5:
"use strict";
var a = [];
var _loop = function(i) {
a[i] = function() {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
a[6](); // 6
So, on seventh iteration for example, the value of i will be 6 (counting from zero). The function created inside the iteration will refer to this value.
I think it would be much better to not define functions in a loop, you could easily accomplish this with one function definition that returns a closure:
function logNumber(num) {
return function() {
console.log(num);
}
}
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = logNumber(i);
}
a[6]();
Regarding the difference between the two examples, one is using let for block scoping. A better example that shows the difference would be:
ECMA5:
for (var i = 0; i < 10; i++) { }
console.log(i); // 10
ECMA6:
for (let i = 0; i < 10; i++) { }
console.log(i); // i is not defined
Edit: as I stated in my comment to your question, this is more likely a side-effect of the transpiler you are using. Firefox supports block scoping and both versions of your loop produce 10 as output (which they should).
This is correct behavior according to the spec. The behavior with var and let is defined to be different.
See the spec, at https://people.mozilla.org/~jorendorff/es6-draft.html#sec-forbodyevaluation. According to this, the relevant concepts, which make the function declared inside the loop close over the current value of the block-scoped loop index, are things called "per-iteration bindings" and "per-iteration environment".
Babel handles it correctly, producing the following code:
var a = [];
var _loop = function (i) {
a[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
This implements the semantics of for (let by isolating the contents of the for loop into a separate function parameterized by the index. By virtue of doing that, the function no longer closes over the for loop index, and i is treated separately in each function created. Thus the answer is 6.
Traceur does not produce the correct result. It yields 10.
So the famous question that has been asked 100 times on SO, about why my function declared in a loop and closing over the index index is using the "wrong" value of the loop index, shall be asked no more?
The issue is a bit more nuanced that merely proclaiming that "of course, let is block-scoped". We know that. We get how it works in an if block, for example. But what's going on here is a bit of an twist on block scoping in the context of a for, hitherto unknown to many people including me. It's a variable actually declared outside the "block" (if you think of the block as the body of the for statement) but has a separate existence inside each iteration of the loop.
For more, see https://github.com/babel/babel/issues/1078.
Why is result different in ES6 and ES5?
Because let and var are different. let is block-scoped while var is function-scoped.
In your first example there is only a single variable i. Every function you create has a reference to the same variable i. At the moment you call a[6](), i has the value 10, because that was the termination condition for the loop.
In the second example, every iteration of the loop has it's own variable i. It works exactly like in other languages with block scope.

Javascript: sort multidimensional array

After creating a multi-dim array like this, how do I sort it?
Assuming 'markers' is already defined:
var location = [];
for (var i = 0; i < markers.length; i++) {
location[i] = {};
location[i]["distance"] = "5";
location[i]["name"] = "foo";
location[i]["detail"] = "something";
}
For the above example, I need to sort it by 'distance'.
I've seen other questions on sorting arrays and multi-dim arrays, but none seem to work for this.
location.sort(function(a,b) {
// assuming distance is always a valid integer
return parseInt(a.distance,10) - parseInt(b.distance,10);
});
javascript's array.sort method has an optional parameter, which is a function reference for a custom compare. the return values are >0 meaning b first, 0 meaning a and b are equal, and <0 meaning a first.
Have you tried this?
location.sort(function(a,b) {
return a.distance - b.distance;
});
Both sort functions posted so far should work, but your main problem is going to be using location as a variable as it is already system defined.

Remove element from Javascript associative array using array value

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();

JavaScript Array Iteration returning more than values

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);
}
}

Categories