I´ll try to be very specific about this topic.
So I have a "for" loop in javascript working just fine. The loop aims to retrieve the URLs of all the files existing in a target folder. The question is, how can I save the retrieved URLs into individual variables?
So, in order to make things easy, I won´t paste the code I´m using, I´ll just create a simple array and run a "for" loop as an example, so you guys can tell me how you would try to save the results into new variables.
So here's the example:
var index;
var arrayElements = ["FirstURL", "SecondURL", "ThirdURL"]
for (index = 0; index < arrayElements.length; index++) {
document.write (arrayElements[index]+"<br/>");
}
So, with that code, I can "print" the different URLs included in the array (I could use console.log of course, but I´m writing in notepad++, so I have to test the code with document.write)
So the question, how can I save each URL into an individual variable?
EDIT:
Ok, so reading the first answers, I think I must clarify some things.
The question is that I need to store the URLs in variables so I can call them later. So it´s not a question about "printing" the URLs.
The function eval()
I know eval is bad
var data = ["a", "b", "c"];
function makeIndvidualVariable(array){
var size;
try{
size = array.length;
for(var i = 0 ; i < size ; ++i){
if( eval(array[i]) != undefined){
eval("var "+array[i]+"="+array[i]+";");
}else{
throw "already exist variable : "+array[i];
}
}
}catch(e){
console.log(e);
}finally{
array = size = i = null;
}
}
makeIndvidualVariable(data);
You can obtain individual variables with window object (if i got you properly).
Use the thing with JS that enables you to declare variables in window scope.
var index;
var arrayElements = ["FirstURL", "SecondURL", "ThirdURL"]
for (index = 0; index < arrayElements.length; index++) {
window['variable'+index] = arrayElements[index];
}
// now variables are available globally (through window object)
document.write(variable0); // prints FirstURL
document.write(variable2); // prints ThirdURL
// etc.
Hope this helps.
Just for the sake of printing the urls stored in array one by one and avoid loops you can use this:
document.write (arrayElements.join("<br/>"));
I am trying to find out the best way to implement nextId() in a Javascript app that issues unique IDs for its objects. If it matters, it's a program that I am doing as a learning experience that's pure JS, HTML, and CSS (no libraries, frameworks, DBMS, etc.). I saw a similar question on here on SO (although I wasn't able to find it again for the link) with answers that included not only storing a list of possible ids paired with a boolean value to determine if the id is used, but also storing deleted ids in a recycling list to use for future objects that need it. I think the latter option sounds better, but I'm sure there are even more ways to do it. Does anyone know if there is a pattern, algorithm, or otherwise best practice for this task?
EDIT:
I would like to allow users to share data at some point soon in the application's life, so IDs that already exist would likely become an issue at some point. I would like the IDs to be permanent as I will be persisting data with LocalStorage. A simple integer will work which I will prefix with a letter or two to identify the type of object. It would also be nice to fill in the holes, so the integer doesn't get too high when users use it long-term (wishful thinking).
Also, all objects are constructed from strings at the beginning of the program (I know it's insane).
If you just need an id that is unique per the lifetime of a page, you can use a simple monotomically increasing counter in the page:
var getUniqueID = (function() {
var cntr = 0;
return function() {
return cntr++;
};
})();
var idA = getUniqueID();
var idB = getUniqueID();
To make sure your ids are unique among all users is a taller task. Without involving a central server that coins unique ids for you, the general concept here is to create an id that is a combination of three things:
A token that is unique to the user (like a userID)
A token that is guaranteed to be unique for the session (like what we have above)
A random value.
Done right, there can never be a collision between two different users (because the userID is in the id) and the counter makes it so no user ever generates the same id twice in the same session and the random value makes the odds of a user generating the same id themselves in the same session extremely small.
var getGUID = (function() {
var cntr = 0;
return function(userID) {
var rand = Math.random().toString().replace(".", "");
return userID + "_" + rand + "_" + cntr++;
};
})();
var idA = getGUID(myUserID);
var idB = getGUID(myUserID);
Note: this is the simpler approach on GUID generation that assumes you already have a userID. There is a whole lot of research and literature on various strategies for generating a GUID which you can certainly read a lot more about if you want something beyond this. Some references on the topic:
http://en.wikipedia.org/wiki/Globally_unique_identifier
http://betterexplained.com/articles/the-quick-guide-to-guids/
http://www.uddi.org/pubs/draft-leach-uuids-guids-01.txt
Depending on the use case, I like to create a complex unique id:
function newGUID(){
var result = '';
var hexcodes = "0123456789abcdef".split("");
for (var index = 0; index < 32; index++) {
var value = Math.floor(Math.random() * 16);
switch (index) {
case 8:
result += '-';
break;
case 12:
value = 4;
break;
}
result += hexcodes[value];
}
return result;
}
You could use UUIDs as IDs. There's one answer here in SO where you can generate UUIDs in JS. UUIDs are usually enough to be used as IDs. Just to be sure that the id isn't a dupe, you can have an object whose keys are the used IDs. As IDs are generated, you can keep track of them by adding them in the object. You can then look them up by doing obj.hasOwnProperty(id).
You can do it like
var idStorage = {};
var id;
// generate ID that's unique and hasn't been used
do{
id = guid();
} while (idStorage.hasOwnProperty(id));
idStorage[id] = true;
// ID is usable
Also, IDs are supposed to be unique. They should never be reused at all.
AngularJS has a very simply approach to generating unique IDs: just increment a global counter. From src/Angular.js:
var uid = 0;
// ...
/**
* A consistent way of creating unique IDs in angular.
*
* Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
* we hit number precision issues in JavaScript.
*
* Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
*
* #returns {number} an unique alpha-numeric string
*/
function nextUid() {
return ++uid;
}
Of course, you should choose a solution depending on what time frame the generated IDs should be unique. The above solution will generate IDs which are unique for one session of one web page. For a simple single-page application (which is Angular's use case), this will do just fine. If you need them to be unique across multiple page loads, or unique for the same user, you'll need to persist the uid for a bit longer (in a cookie or in a database). If they need to be unique for a longer time, you might also need to look into longer IDs with more than 2^53 possible values.
I would use a UUID v4 since these IDs would always be unique in any circumstance (no additional logic to check if these ids are in use or to recicle old ones), just issue a new id whenever you need one.
Very simple implementation in JS as follows:
function generateUUID(){
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}
$(function() {
$('button').bind('click', function() {
$('input').val(generateUUID());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" style="width: 350px;"/><button>GENERATE NEW ID</button>
Well, it's a bit unclear what you expect from these ids, however if you only want a unique id per type of entity in your system and your data must only live in memory, then you could use the following approach:
Note: I saw from the comments you wanted a prefix which represents the entity.
function identitySequence(base) {
var id = base || 0;
return function () {
return ++id;
};
}
//Lets say you had some sort of repository for every entity
function InMemoryUserRepository(nextId) {
this._nextId = nextId;
}
InMemoryUserRepository.prototype = {
constructor: InMemoryUserRepository,
get nextId() { return 'user-' + this._nextId(); }
//other operations, like save...
};
var userRepository = new InMemoryUserRepository(identitySequence());
userRepository.nextId; //user-1
Now, lets say you wanted your id sequence to be persistent in the localStorage, here's what you could do (extending the above code):
var userRepository = new InMemoryUserRepository(localStorageIdentitySequence('userIdSeq'));
userRepository.nextId; //user-1
//Reload page
userRepository.nextId; //user-2
function localStorageIdentitySequence(storageKey) {
var next = identitySequence(+localStorage.getItem(storageKey));
return function () {
return +(localStorage[storageKey] = next());
};
}
This works fine for a single machine, however if you want a unique id generator that will generate unique IDs across machines, then this approach will not work. You would have to generate the ID from a server that can be accessed by all clients, or you can generate a GUID on the client instead. There would be no way to know if the GUID was already generated by another client, but that would be very unlikely.
Please forgive me if this is answered on SO somewhere already. I've searched, and it seems as though this is a fairly specific case.
Here's an example of the JSON (NOTE: this is very stripped down - this is dynamically loaded, and currently there are 126 records):
var layout = {
"2":[{"id":"40","attribute_id":"2","option_id":null,"design_attribute_id":"4","design_option_id":"131","width":"10","height":"10",
"repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"},
{"id":"41","attribute_id":"2","option_id":"115","design_attribute_id":"4","design_option_id":"131","width":"2","height":"1",
"repeat":"0","top":"0","left":"0","bottom":"4","right":"2","use_right":"0","use_bottom":"0","apply_to_options":"0"},
{"id":"44","attribute_id":"2","option_id":"118","design_attribute_id":"4","design_option_id":"131","width":"10","height":"10",
"repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"}],
"5":[{"id":"326","attribute_id":"5","option_id":null,"design_attribute_id":"4","design_option_id":"154","width":"5","height":"5",
"repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"}]
};
I need to match the right combination of values. Here's the function I currently use:
function drawOption(attid, optid) {
var attlayout = layout[attid];
$.each(attlayout, function(k, v) {
// d_opt_id and d_opt_id are global scoped variable set elsewhere
if (v.design_attribute_id == d_att_id
&& v.design_option_id == d_opt_id
&& v.attribute_id == attid
&& ((v.apply_to_options == 1 || (v.option_id === optid)))) {
// Do stuff here
}
});
}
The issue is that I might iterate through 10-15 layouts (unique attid's), and any given layout (attid) might have as many as 50 possibilities, which means that this loop is being run A LOT.
Given the multiple criteria that have to be matched, would an AJAX call work better? (This JSON is dynamically created via PHP, so I could craft a PHP function that could possibly do this more efficently),
or am I completely missing something about how to find items in a JSON object?
As always, any suggestions for improving the code are welcome!
EDIT:
I apologize for not making this clear, but the purpose of this question is to find a way to improve the performance. The page has a lot of javascript, and this is a location where I know that performance is lower than it could be.
First and foremost you should measure and only act if there is a real performance concern. You need exact numbers like 200ms or 80% time is spent there. "It runs a lot" doesn't mean anything. The browser can loop very fast.
You can improve constant factors as others mentioned, like using a native for loop instead of jQuery.each. Chaching global variables won't help you too much in this case.
If you really want to improve efficency you should find a better algorithm than O(n). Assuming that you only use this data for finding elements matching a certain criteria you can use JS objects as hashes to achive a O(1) performance.
Just an example for you specific case:
var layout = {
"2": { "2,,4,131,10,0": ["40", "93"], "2,115,4,131,0": ["41"] },
"4": { ... },
...
};
It's fairly easy to generate this output in php, and then you just use a lookup to find ids matching your particular criteria.
IMHO, a simple hashmap index will probably work best. This does require you to loop over the data ahead of time, but the index can be easily appended to and cached.
Once the index is generated, this should be O(1) for lookups, and will handle multiple entries per key.
var layout = {
"2":[[...], ...],
"5":[[...], ...]
};
var index = {};
function makeKey(data) {
return data.join('_');
}
for(var l in layout) {
var cur = layout[l];
for(var i in cur) {
var item = cur[i];
var key = makeKey([item.p1, item.p2, ...]);
index[key] = index[key] || [];
index[key].push(item);
}
}
function find(attid, optid) {
var key = makeKey([attid, optid, 1, d_att_id, ...]);
return index[key]; //this is an array of results
}
My first suggestion would be to stop using $.each if you want to squeeze out every bit of performance you can. jQuery.each does a bit more than a traditional loop. Take a look at this jsFiddle with your browser's debugger running (i.e. Safari's/Chrome's web developer tools) and step through the fiddle until execution fully returns from jQuery.
For reference, the fiddle's code is:
var myArr = [{"foo":"bar"},{"answer":42}];
debugger;
$.each(myArr, function(k, v) {
console.log(k + ': ');
console.dir(v);
});
Now run through the second version:
var myArr = [{"foo":"bar"},{"answer":42}],
i, j;
debugger;
for (i = 0, j = myArr.length; i < j; i += 1) {
console.log('myArr[' + i + ']: ');
console.dir(myArr[i]);
}
Notice that there are far fewer operations being executed in the second version. So that's a tiny bit of performance gain.
Second, eliminate as many lookups outside of the local scope as you can. Even if you cache a reference to your global variables (boo!) then you can save a lot of time given that this loop will be executed possibly hundreds of times. So instead of:
function foo(a, b) {
if (a === globalA && b === globalB) {
// Do stuff
}
}
You'd do:
function foo(a, b) {
var gA = globalA,
gB = globalB;
if (a === gA && b === gB) {
// Do stuff
}
}
As for pairing down the conditional based on the object members, I'm not seeing much else that could be improved. The object properties you are checking are top level, and you're looking at local instances of each object (so the scope chain lookups are short).
Without knowing more about how this is actually supposed to work, those are the best recommendations I can make. However, I can make the guess that your idea of starting with simpler JSON data would be a big improvement. If you know what the layout, and its constraints, is, then requesting the specific details from the server would mean you don't have to check so many conditions. You could simply ask the server for the details that you actually need to implement and loop through those (to update the DOM or whatever).
I see that the you are searching by
5 fields: v.design_attribute_id,v.design_option_id,v.attribute_id,v.apply_to_options,v.option_id.
What you could do is add an extra field to the objects called "key" that is a composite of the values in those fields.
Here's an example
{
"key": "4_131_2_0_0" //i picked 0 to represent null, but you can pick any number
"id": "40",
"attribute_id": "2",
"option_id": null,
"design_attribute_id": "4",
"design_option_id": "131",
"width": "10",
"height": "10",
"repeat": "0",
"top": "0",
"left": "0",
"bottom": "0",
"right": "0",
"use_right": "0",
"use_bottom": "0",
"apply_to_options": "0"
}
Note though that you must normalize the length of each value.
Meaning that if one objects optionId is 1 and another object optionID is 566 you must represent
the first optionId as 001 in the key string.
With this field you can then sort the array on the server side before returning it to the client.
Then you can use a binary search to find the values on the client.
Using the binary search implementation located here
http://www.nczonline.net/blog/2009/09/01/computer-science-in-javascript-binary-search/
Your search function would look something like
function drawOption(attid, optid) {
var attlayout = layout[attid];
var needle = d_att_id + "_" + d_opt_id + "_" + attid + "_" + optid; //remember to normalize length if you have to
var idx = binarySearch(attlayout,needle);
var item;
if(idx !== -1){
item = attlayout[idx];
//do something
}
}
Another method you can try using this composite key idea is to have the server return
the layout objects in one big object mapped by
attid,v.design_attribute_id,v.design_option_id,v.attribute_id,v.apply_to_options,v.option_id
Then you can look up in O(1) time.
It would look something like
function drawOption(attid, optid) {
var needle = attid + "_" + d_att_id + "_" + d_opt_id + "_" + attid + "_" + optid; //remember to normalize length if you have to
var item = layout[needle];
if(typeof item !== "undefined"){
//do something
}
}
When trying to improve your code, it is always better to check which functions are taking time using firebug profiling. You can either profile by clicking on profile button in firebug's console panel and then run your code or using firebug's profiling commands in your code
From the code that you have given, only a few improvement points can be given.
$.each is slow compared to native looping solutions. For the best
looping solutions, check out this JsPref test
It would be better to change the JSON to use arrays instead of object literals. It is said to be more faster to retrieve values.
I have experience to such issue before, my js array of objects consist of 8 thousands record and more.
My experience is not about the performance, but the readability, maintainability, scalable of the codes.
hence I developed an JS Object Query Library 2 years ago: JSOQL
http://code.google.com/p/jsoql/
It works like SQL to allow you query your js array of objects with syntax similar to SQL.
The example usage is something like this, I cant really remember, but you can download the example usage in the download tab.
new JSQOL().Select([field1, field2, field3 ...]).From({ ... }) .Where(fn) .Offset(int) .Limit(int) .Get();
Note:
{...} is your array of objects, or an object it self.
Hope it helps, you can send me message if you need more information.
It's not going to work everywhere but your problem sounds like something that can be done with webworkers
Another thing I would look at if you dont have webworkers is trying not to block the ui to long. If you can chunk it into bits of about 40ms and then setTimeout the next chunck for just a few ms later the user will have a more pleasant experience. This needs a little fiddling but users will start to notice stuff when something takes longer than somewhere between 50 and 100ms
Have you considered using the jQuery grep function?
jQuery grep
And jquery grep on json object array for an example.
Here is one technique that will probably yield better performance at the expense of using a bit more memory.
I'll leave my code examples simple just to illustrate the concept.
First, you'll want to pre-process your JSON data into some additional arrays that act as indexes. Here is an example of what the final arrays might look like after pre-processing:
var layouts_by_attribute = {
// attribute_id => array(layouts)
2: [40, 41, 44],
5: [326]
};
var layouts_by_design_attribute_id = {
// design_attribute_id => array(layouts)
4: [40, 41, 44, 326]
};
Finding a layout by attribute is now very quick:
function findByAttribute(attribute_id) {
return layouts = layouts_by_attribute[attribute_id];
}
function findByDesignAttribute(design_attribute_id) {
return layouts = layouts_by_design_attribute[design_attribute_id];
}
I'm using the custom connection method (Raphael.fn.connection) added in the example found at: raphaeljs.com/graffle.html
My example is here: http://jsfiddle.net/WwT2L/ (scroll in the display window to see the effect)
Essentially, I've linked the graffle connection to the bubble so it stays with it as it scales. I'm hoping that I can have the connection switch to the next bubble as the user scrolls past a certain point.
To do this, I was thinking I would remove the connection and add another one, but as the connection method is not a native Raphael element, it doesn't have the built in remove method, and I'm having trouble adding the remove method to the prototype.
I've found some info about adding custom methods at this google group discussion
and I've tried:
this.connections[0] = this.r.connection(this.bubbles[0], this.unitConnector, "#fff", "#fff").__proto__.remove = function() {alert('working custom method');};
which seems to add a method to this instance of connection but I'm not sure what to have the method do and it seems like there should be a better way.
To recap... when we create a connection, we often use the following:
connections.push(
r.connection(r.getById(firstObjectId), r.getById(secondObjectId), '#fff')
);
What we're doing here is pushing (adding) a Raphael.connections object into a connections[] array, based on their Raphael object id's
To add a method/function to Raphael, one might use:
Raphael.fn.fnName = function (){ /* Your code here */ }
This creates a function in our Raphael namespace for use with our Raphael objects.
Below is the code i've created which does exactly what you require. I couldn't find a good resource out there for Raphael, but will surely be creating one soon, as I have done a lot of development with it.
Raphael.fn.removeConnection = function (firstObjectId, secondObjectId) {
for (var i = 0; i < connections.length; i++) {
if (connections[i].from.id == firstObjectId) {
if (connections[i].to.id == secondObjectId) {
connections[i].line.remove();
connections.splice(i, 1);
}
}
else if (connections[i].from.id == secondObjectId) {
if (connections[i].to.id == firstObjectId) {
connections[i].line.remove();
connections.splice(i, 1);
}
}
}
};
Just like in the create connections, two id's are provided. We must find these ID's in the array of connections we've pushed each connection set to. If you only have one connection, there is no need for array traversing, though this is a case less encountered.
We have two possible scenarios here - excluding the case of having found no connection for simplicity sake. It either finds that:
the connection objects from.id corresponds to the first provided paramenter firstObjectId. Then, the to corresponds to the second provided paramenter secondObjectId.
the connection objects from.id corresponds to the first provided paramenter secondObjectId. Then, the to corresponds to the second provided paramenter firstObjectId.
This method of checking covers all our bases, so no matter how the connection is interacted with (in my case the user clicks two objects to connect them, and delete their connection)
Once we've confirmed we have the two correct objects, we then remove the line from the DOM, using connections[i].line.remove(); as just removing the connection object from the array will leave it on the map.
Finally, we remove the specified connection object from the array, and the splice method leave us with an un-holy array (no holes in our array, that is ;) ) using connections.splice(i, 1);
Then,
this is what i am using to remove connections from connections array used with graffle example and so far i am having no issue with it. the question may be old but i stumbled upon on it searching the related solution, so when i had no luck i created mine and want to share with rest of the world.
//checks if the current object has any relation with any other object
//then remove all the to and from connections related to current object
for(var i =0 ; i<connections.length; i++){
if(connections[i].from.id == objectId || connections[i].to.id ==objectId ){
connections[i].line.remove();
}
}
//finds out which connections to remove from array and updates connections array
connections = $.grep(connections, function(el){
return el.line.paper != null;
})
the splice method was having issues with my case as if object has more than one connections (to, from) with multiple objects and every time i was using splice the main connections array length was changing as well as value of i was increasing, so i used jQuery grep method to update array based on removed lines. i hope this will help others too.
function removeShape(shape) {
//CONNECTIONS is my global structure.
var connections = [];
while (CONNECTIONS.length) {
var connection = CONNECTIONS.pop();
if (connection.from.id == shape.id || connection.to.id == shape.id)
connection.line.remove();
else
connections.push(connection);
}
shape.remove();
CONNECTIONS = connections;
}