JS looping and populating array. Which is faster? - javascript

I just saw a video of Nicholas Zakas of Yahoo, at GoogleTalks talking about speeding up your website. One of the things he mentioned was doing loops in reverse order to skip one of two comparisons: for (i = len; i--;) {}
And he said to keep away from JS libraries implementations of for each. Just for fun I thought I'd try it out. Turns out he was wrong.
var array1 = new Array();
var array2 = new Array();
var start = 0;
var finished = 0;
start = (new Date).getTime();
$("#newDivTest").children().each(function(i){
array1[i] = $(this).get(0).id;
});
finished = (new Date).getTime() - start;
alert(finished);
start = (new Date).getTime();
var len = $("#newDivTest").children().length;
for (i = len; i--;) {
array2[i] = $(this).get(0).id;
}
finished = (new Date).getTime() - start;
alert(finished);
newDivTest holds 1000 empty divs with an id starting at "0" and going up to "999". Another note is that $(this).get(0).id is about 3 times faster than $(this).attr("id") for some reason, anyone know why?
For FF3.5, the results are "7" and "45", IE7 gives "30" and "45", Chrome2 gives "4" and "17", Opera10 gives "16" and "16", and lastly Safari4 gives "4" and "16".
So it seems the approach Nicholas is hardest against is actually the faster in almost all instances.
I'm not smart enough to know what's going on behind the scenes for jQuery's each()-method, but it must be doing something right...right?

One flaw in your setup is that your second test will not actually work. You wrote:
for (i = len; i--;) {
array2[i] = $(this).get(0).id;
}
But this is not defined there, so the entire operation will fail. You'd have to do something like:
var children = $("#newDivTest").children();
for (i = children.length; i--;) {
array2[i] = children.get(i).id;
}
And this gets at a more pressing issue than performance: although calls to something like jQuery's .each() function do result in added function calls (and the associated added overhead), they also tend to make it much easier to express what you want the code to do.
Quoting Michael Jackson: "The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet."

Aren't your tests doing different things?
In the second test this is not the same as the first one.

Slightly off topic and not a direct answer to your main question but, jQuery's each method is implemented like so (jQuery 1.3.2)
jQuery.extend({
/* ... Code taken out for brevity ... */
// args is for internal usage only
each: function( object, callback, args ) {
var name, i = 0, length = object.length;
if ( args ) {
if ( length === undefined ) {
for ( name in object )
if ( callback.apply( object[ name ], args ) === false )
break;
} else
for ( ; i < length; )
if ( callback.apply( object[ i++ ], args ) === false )
break;
// A special, fast, case for the most common use of each
} else {
if ( length === undefined ) {
for ( name in object )
if ( callback.call( object[ name ], name, object[ name ] ) === false )
break;
} else
for ( var value = object[0];
i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
}
return object;
}
/* ... Code taken out for brevity ... */
);
as you can see, a callback function is applied to each property of object. the jQuery object has a length property defined so will perform the following loop (generally, no args are supplied)
for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
in each iteration, the callback function will increase the scope chain length by 1, thus will take longer to resolve the reference to the object's property.

I notice that your question is "JS looping and populating array. Which is faster?", but your examples are actually testing the speed of various selectors of JQuery, right? You might be interested in checking out :http://mootools.net/slickspeed/
As for "JS looping and populating array. Which is faster?", see here : http://blogs.oracle.com/greimer/resource/loop-test.html

Related

Extendscript batch save PDFs

I need a little help here. I want a script that converts all the AI files in all the subfolders into PDFs and puts them in a single folder at the root and I want it to skip any folders named "Resources". I found a script that mostly does what I need it to do and I have modified it further to suit our exact needs. I'm struggling with 2 parts of it though. Specifically lines 33 and 80 are my issues now.
#target illustrator
main();
//
function main() {
var topLevel = Folder.selectDialog( 'Select the top level folder to start converting AI files to PDFX files' );
var proofFolder = new Folder( topLevel + '/Proofs2/');
proofFolder.create();
if ( topLevel != null ) {
processDocs( recursiveFolders( topLevel, /\.ai$/i ), getPDFOptions() );
};
};
//
function processDocs( aiFiles, opts ) {
var i, baseName, doc, saveFile;
for ( i = 0; i < aiFiles.length; i++ ) {
doc = app.open( aiFiles[i] );
baseName = decodeURI( doc.name.match( /(.*)\.[^\.]+$/ )[1] );
//This is line 33// saveFile = File( proofFolder.path + baseName + '.pdf' );
doc.saveAs( saveFile, opts );
doc.close( SaveOptions.DONOTSAVECHANGES );
};
};
//
function getPDFOptions() {
var pdfSaveOpts = new PDFSaveOptions();
pdfSaveOpts.acrobatLayers = true;
pdfSaveOpts.colorBars = false;
pdfSaveOpts.colorCompression = CompressionQuality.AUTOMATICJPEGHIGH;
pdfSaveOpts.compressArt = true;
pdfSaveOpts.embedICCProfile = true;
pdfSaveOpts.enablePlainText = true;
pdfSaveOpts.generateThumbnails = true;
pdfSaveOpts.optimization = true;
pdfSaveOpts.pageInformation = true;
pdfSaveOpts.preserveEditability = true;
pdfSaveOpts.pDFXStandard = PDFXStandard.PDFX1A2001;
pdfSaveOpts.viewAfterSaving = false;
return pdfSaveOpts;
};
//
function recursiveFolders( fold, exp ) {
var fileList = Array(); // Our matching files…
getFiles( fold, exp, fileList );
return fileList;
};
//
function getFiles( fold, exp, array ) {
//This is line 80// if (Folder.name !== /\Resources$/){
var i, temp;
temp = Folder( fold ).getFiles(); // All files and folders…
for ( i = 0; i < temp.length; i++ ) {
if ( temp[i] instanceof File && RegExp( exp ).test( temp[i].fsName ) ){
array.push( temp[i] );
};
if ( temp[i] instanceof Folder ) {
getFiles( temp[i].fsName, exp, array );
};
};
return array;
};
};
Line 33:
Variables in Javascript are local to the function they are defined in. That is, if you try
a();
function a()
{
var a1 = 1;
b();
}
function b()
{
alert ("var a1 = "+a1);
}
you will find a1 is undefined inside function b. Two common solutions (there may be more) are to put the variable in the function definition:
b(a1);
..
function b (myVarArg)
{
alert ("var a1 = "+myVarArg);
}
or -- simple but slightly error-prone -- declare the variable at the very top of your program before main:
var a1;
This is a quick and simple solution; the variable will be 'visible' inside all functions, and assigning a new value anywhere will work. It is 'slightly error-prone' because a local variable with the same name (defined inside a function) will effectively 'hide' the original. Thus, if used careless, you could end up with a construction such as this:
var a1 = 1;
a();
alert ("var a1 = "+a1);
function a()
{
var a1 = 2;
b();
alert ("var a1 = "+a1);
}
function b()
{
alert ("var a1 = "+a1);
}
Fortunately, your use of clear, descriptive variable names will help you here.
Line 80
if (Folder.name !== /\Resources$/) ...
Improper use of proper Javascript syntax ☺
!== is not a valid comparison operator. Use either == (Test if Equal) or === (Test if Very Equal -- see Does it matter which equals operator (== vs ===) I use in JavaScript comparisons? for some gritty details), and != for Test if Not Equal.
You can compare a string to another but not to a GREP expression. The notation /.../ is reserved for GREP expressions only, for strings you need "..." or '...'. While it's true that some functions may accept both GREP and regular strings, this test does not.
To use a GREP string in a comparison, use the String function match:
if (Folder.name.match(/\\Resources$/)) ...
Note the double end parentheses (one for the enclosing if and one for the match function) and double backslashes, because the backslash is a 'special character' inside a GREP string. (It is special inside a Javascript string as well, so whatever you intended to use, it possibly could be called Improper Syntax #4.)
Will this Fix Everything and Make It Work?
Untested. Without these corrections, your script will not work. With them, it should at least do something (if your logic is sound, your paths exist, your input is right and your Illustrator constants have the correct name).
Provisionally, I'd hazard to say it should work.
Addendum
It took some debugging to find out the real problem .. Debug strategy: inserting alert before suspicious lines, showing values of important variables. Run, wait for results, rinse, repeat.
First off: passing on array as a function parameter seems to have been a Not-Good idea. I found that the array got copied over and over onto itself (or perhaps it was just the result of my tinkering). I think the safer way is to have getFiles return only the newly-added files, and concatenate the result to its current version (i.e., "in" the routine from whence you called getFiles, where it doesn't matter if it was a previous incarnation of getFiles or not). See (I think!) my warning on modifying 'local', 'global', and/or 'passed' variables above.
function getMyFiles( fold, exp ) {
//This is line 80//
if (!fold.name.match (/(\\|\/)Resources$/)) {
var i, temp;
var array = [];
temp = Folder( fold ).getFiles(); // All files and folders…
for ( i = 0; i < temp.length; i++ ) {
if ( temp[i] instanceof File && RegExp( exp ).test( temp[i].fsName ) ){
// alert ('path '+fold+', adding '+temp[i]);
array.push( temp[i] );
};
if ( temp[i] instanceof Folder ) {
array = array.concat (getFiles( temp[i], exp ));
};
};
// alert ('array is '+array.length+'\n'+array.join('\n'));
return array;
};
return [];
};
You would call this function as before in your original recursiveFolders function, but this time don't pass the array but assign it:
function recursiveFolders( fold, exp )
{
var fileList = Array(); // Our matching files…
fileList = getFiles( fold, exp );
// alert ('Filelist is '+fileList.length+'\n'+fileList.join ('\n'));
return fileList;
};
Only when I got that part working, I got the same error you did, per your comment "fold.match is not a function". That took a minute or so of head-scratching. As it turns out, the Javascript parser was right. Initially, the fold argument is a Folder and to get its name, you need fold.name. But look in your own code: you used in the recursive call to itself the argument temp[i].fsName, which is a String! Hence, no name, hence, error.
First, I bluntly converted fold to always-a-String using fold.toString (which works on Folder and String alike), but then I changed my mind and used fold.name for consistency, and call getFiles with the "proper" argument: temp[i] instead of temp[i].fsName. Now it seems to work -- for real. (Said with slightly more confidence.)

Dynamically Assign Variables after looping through JSON object

Probably a beginner question, but I have an array json object that I'm looping through to pull out certain values. If these values equal "NO", then I want to hide a div using jquery. This should be simple enough, but I don't know how many items the array can contain so I'll have to make sure to dynamically grab all of the number/value pairs.
My JSON is similar to below:
"account" : [{
"ID":1,
"transferAllowed": "NO"
},{
"ID":2,
"transferAllowed": "YES"
}]
My loop:
//define variable that references the values I need from the json object
var account = this.json.account;
for ( var i = 0; i < account.length; i++ ) {
//compare all values of transferAllowed however many that may be. If they all equal No, then hide div with jquery $("#div").hide()
}
Any help is appreciated. Thanks.
Try flipping the problem, like defining a "null hypothesis" for a science experiment: to disprove that all items say "NO", you need only find a single value that doesn't say "NO".
This means that you only need a simple check in the loop to find your answer:
var account = this.json.account;
// Start with the hypothesis that everything will say "NO"
var allDisallowed = true;
for ( var i = 0; i < account.length; i++ ) {
if ( account[i].transferAllowed != "NO" ) {
// Hypothesis disproved!
allDisallowed = false;
}
}
if ( allDisallowed ) {
$("#div").hide();
}
Since you only need one value to be allowed, you can actually stop at the first one you see using a break; statement:
for ( var i = 0; i < account.length; i++ ) {
if ( account[i].transferAllowed != "NO" ) {
// Hypothesis disproved!
allDisallowed = false;
// No need to finish the loop
break;
}
}
Alternatively, if you wrap the loop up in a function, you can save defining some variables:
function areAllDisallowed( account ) {
for ( var i = 0; i < account.length; i++ ) {
if ( account[i].transferAllowed != "NO" ) {
// At least one element is not a "NO", so result is false
return false;
}
}
// We will only reach this line if we never return false
// Therefore, we know that all elements say "NO"
return true;
}
if ( areAllDisallowed(this.json.account) ) {
$("#div").hide();
}
(As pointed out in a comment, the position of var i in the for loop could cause confusion later, but I've left it as-is to make only the changes that answer your specific question.)
try this :)
var account = this.json.account;
var hide_count = 0;
for ( var i = 0; i < account.length; i++ ) {
if(account[i].transferAllowed=="NO") hide_count++;
}
if(hide_count == account.length){
$("#div").hide();
}

A possible solution for function looping

I was reading John Resig's Learning Advanced JavaScript slides.
As i came to the slide-27, john presents a quiz as per below :
QUIZ: How can we implement looping with a callback?
function loop(array, fn){
for ( var i = 0; i < array.length; i++ ) {
// Implement me!
}
}
var num = 0;
loop([0, 1, 2], function(value){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
I tried to implement, and came up with following code :
function loop(array, fn){
for ( var i = 0; i < array.length; i++ ) {
fn.call(array, array[i]);
}
}
var num = 0;
loop([0, 1, 2], function(value){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
I was happy that it worked, and eager to see the next slide to compare it with solution john will provide in next slide.
but in next slide john provided the following solution :
function loop(array, fn){
for ( var i = 0; i < array.length; i++ )
fn.call( array, array[i], i );
}
var num = 0;
loop([0, 1, 2], function(value, i){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
to the fn function he passed to loop(), he added another parameter i.
that makes me wonder why another parameter is required ??
In a normal for loop, the body has access to the index i. If the callback solution is intended as a replacement for this, it should also have this information available. It could be useful for creating unique identifiers, for instance. So we pass it as a parameter.

Bad juju with for/in loop

I have a small script that looks like this:
hideElements = arguments.shift().split ( ',' );
for ( iterator in hideElements ) {
console.log (' --> hiding ' + hideElements[iterator] );
lg_transitions ( {kind:"slide-up"} , { target : hideElements[iterator] } );
}
When I run it in the debugger all things start quite rationally. I put a breakpoint at the first line listed above. After pressing the "step over next function call" button to initialise the "hideElements" variable to the following:
This is what is what I would have expected but then after completing the first (and should be the only) iteration it comes back to the head of the loop and the "iterator" which had started at 0 has now strangely changed to "remove". Huh? No idea where that came from. But in the console.log message that follows there might be a hint ... it prints the following to the console:
This is a function called -- you guessed it -- "remove". It is a function that I added recently for a different reason but it is not called directly or indirectly and so I'm at a loss as to why this would be picked up here. For anyone interested in the full code for "remove", here it is:
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
}
ADDITION:
The code I had neglected to add earlier was the initialisation of the arguments array. Here's what I had (note I've since changed the name to "args" instead of arguments):
function ConditionalFormatting ( key , eventObject , setOfRules ) {
console.log ("Entering conditional formatting: key is " + key + ", eventObject is " + eventObject.attr('id') + ", setOfRules is " + setOfRules );
var ruleStrings = [];
ruleStrings = setOfRules.split (';');
var targetOverride = false;
jQuery.each ( ruleStrings , function ( i , ruleString ) {
// There is a rule, now let's find out which one
var targetElement;
var args = [];
args = ruleString.split('::');
var rule = args.shift();
#Yoshi is right: for in will list all fields in the object - which is what the array actually is.
Try using the hasOwnProperty method:
hideElements = arguments.shift().split ( ',' );
for (iterator in hideElements ) {
if (hideElements.hasOwnProperty(iterator))
{
console.log (' --> hiding ' + hideElements[iterator] );
lg_transitions ( {kind:"slide-up"} , { target : hideElements[iterator] } );
}
}
This is why you should usually avoid using for in to iterate over arrays and use a normal for loop instead. jsHint/jsLint will give you a message like this for the code you posted:
The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.
Sidenote:
for (iterator in hideElements ) will create a global variable iterator, while for (var iterator in hideElements ) won't.
The arguments object is not an actual array. Therefore it doesn't have the shift() function. If you want the first object in the array, obtain the element at index 0 (first object). Moreover, use a regular for loop to traverse the arguments object.
hideElements = arguments[0].split(',');
for ( var i = 0; i < hideElements.length; i++ ) {
...
}
arguments is not a real array, however, if you wish to use it like it were an array, then use Array.prototype.slice.call( arguments );:
if ( arguments.length > 0)
hideElements = Array.prototype.slice.call( arguments ).shift();
From there on you can use hideElements as an array.

JavaScript; n-dimensional array creation

In the process of building a JavaScript interpreter for a simple language, I've faced the following problem;
After parsing, we get an array of indices that specifies the element in an n-dimensional array to be modified. For instance, after parsing this:
a[1, 1, 1]
We get an array [1, 1, 1]. The language I'm working on doesn't have variable definitions, so variables get initialized on their first use. My goal is to be able to create this n-dimensional array so that I can place it in the variable table (in the example above, we'd need to create a 3-dimensional array).
The short question:Is there a way to create an n-dimensional array in JavaScript without using eval()?
Tested in Chrome:
function createNDimArray(dimensions) {
if (dimensions.length > 0) {
var dim = dimensions[0];
var rest = dimensions.slice(1);
var newArray = new Array();
for (var i = 0; i < dim; i++) {
newArray[i] = createNDimArray(rest);
}
return newArray;
} else {
return undefined;
}
}
Then createNDimArray([3, 2, 5]) returns a 3x2x5 array.
You can use a similar recursive procedure to access an element whose index is in an array:
function getElement(array, indices) {
if (indices.length == 0) {
return array;
} else {
return getElement(array[indices[0]], indices.slice(1));
}
}
Setting an element is similar, and left as an exercise for the reader.
There's nothing built in, but it's pretty easy to create a function that would do the job:
var genArray = function () {
var arr, len, i;
if(arguments.length > 0) {
len = [].slice.call(arguments, 0, 1)[0];
arr = new Array(len);
for(i = 0; i < len; i++) {
arr[i] = genArray.apply(null, [].slice.call(arguments, 1));
}
} else {
return null; //or whatever you want to initialize values to.
}
return arr;
};
var a = genArray(3, 2); //is [[null, null],[null, null],[null, null]]
var b = genArray(3, 1, 1); //is [[[null]],[[null]],[[null]]]
a[0][1]; //is null
b[1][0][0]; //is null
b[1][0][0] = 3;
b[1][0][0]; //is 3;
b; //is [[[null]],[[3]],[[null]]]
Maybe that will help?
PS --
I know this might seem like more effort than is necessary. But unfortunately, JavaScript arrays are not really "arrays" (if by "array" you mean a contiguous, indexed, immutable memory block). They're more like "maps" in most languages. So there's a certain amount of effort involved in creating them. Most languages have no problem creating multi-dimensional arrays because they're just doing some simple multiplication followed by an malloc(). But with JavaScript, you really have to go recursively generate your arrays if you want to have them pre-constructed. It's a pain, but it does demonstrate the effort required by the interpreter.
Go figure.
For creating an n-dimensional array:
function createNDimArray(dimensions) {
var ret = undefined;
if(dimensions.length==1){
ret = new Array(dimensions[0]);
for (var i = 0; i < dimensions[0]; i++)
ret[i]=null; //or another value
return ret;
}
else{
//recursion
var rest = dimensions.slice(1);
ret = new Array(dimensions[0]);
for (var i = 0; i < dimensions[0]; i++)
ret[i]=createNDimArray(rest);
return ret;
}
}
EDIT: Due to the fact that any recursive solution will have a limit to the size of the array you can create... I made another solution in my
PJs # GitHub library. This one runs at pseudo-instant speed and can create and manage a multidimensional array of any size, any structure, with any dimensions at any branch. It also can simulate prefilling and/or use a node object of custom design. Check it out here: https://github.com/PimpTrizkit/PJs/wiki/14.-Complex-Multidimensional-Object--(pCMO.js)
Using a modified version of jfabrizio's solution:
function createNDimArray(dimensions) {
var t, i = 0, s = dimensions[0], arr = new Array(s);
if ( dimensions.length < 3 ) for ( t = dimensions[1] ; i < s ; ) arr[i++] = new Array(t);
else for ( t = dimensions.slice(1) ; i < s ; ) arr[i++] = createNDimArray(t);
return arr;
}
Usages:
var arr = createNDimArray([3, 2, 3]);
// arr = [[[,,],[,,]],[[,,],[,,]],[[,,],[,,]]]
console.log(arr[2][1]); // in FF: Array [ <3 empty slots> ]
console.log("Falsy = " + (arr[2][1][0]?true:false) ); // Falsy = false
I found this to be quite a bit faster. I might stretch to say that it could be the fastest way possible to generate a N Dimensional array in Javascript. This refactoring above had some good speed increases. But, the best speed increase came from not prefilling, of course. This version doesn't prefill the array. It only returns a fully created N dimensional array of Ns lengths where the last level is just an empty array. I would hope that arr[x][y][z]?arr[x][y][z]:null is sufficient if you really need the null value. It is for my uses. :)
If you need prefilling, use his original version.
And, if you don't really care about what I did; then stop reading.
Want more geek talk? A little something about recursion for those learning out there. Alright here are the tactics. When doing deep recursion, keep in mind the final level. Its where most of the work is done. In this case its the Nth dimension, literally. This is your "payload", the rest is logistics. In jfab's function, when dimensions.length gets to 1, its last dimension, its in the Nth dimension and performs the payload. Which is to create the array of nulls, or in my case, an empty array. Since the recursion gets so deep each dimension is a factor of the last one. By the time you get to the Nth dimension you will have a lot of function calls and logistics gets cumbersome to the computer. And at the Nth dimension you will call your base recursion function (createNDimArray in our case) more times for the payload than you will for logistics. Now, as in jfab's original solution, putting the execution of the payload as the very first thing you do in recursion (if possible) is usually a good thing, especially if its simple. Here, by making the payload a building of the final 2D array (instead of just a 1D array by simply returning a new Array() only). Then the excessive amount of function calls now don't have to happen at this level. Now, of course, if you want to prefill the array, then this shortcut doesn't always help. But more to the point, prefilling the array would be the appropriate payload. By not visiting every item on the Nth dimension we have effectively removed it. That way there is one less level of function calls and basically the Nth dimension's payload is actually done on the N-1 th Dimension. And we are never calling the recursive function again just to deliver the new Array(). Unfortunately, the call to new Array(x) (in general) doesn't see it that way. Its execution time does increase with a larger x. Which is effectively still visiting every item in the Nth Dimension, but now we do it only once and with native code and wrapped in a tight and light loop. Now we require that createNDimArray can only be called with N > 1, ie never used to create 1D arrays. Theoretically you could require a larger N, and unroll even more dimensions at the end. Basically, the line with if ( dimensions.length < 3 ) will read something like < 4 or < 5 and you would have to wrap that many more for loops around the one thats there, and they would each all need their own set of vars --- so I'm not sure how efficient it all might be, as you are trading excessive function call and stack space/manipulation with a similar idea but in embedded for loops --- But I suppose it could speed up some environments if you know that N is always above a certain level or if its only for the final dimensions. Like here, I did it for the last two dimensions. But if you unroll too much, then your payload itself is a bear. Only testing will tell if thats worth it. It does seem that stack space is limited, and I think I remember having been able to make larger arrays with more unrolling. There is a limit to how big you can make an array. And recursion solutions that call themselves for each item at the Nth level had the lowest limit if I do.. recall.. correctly.... much lower.
The next part in revising his solution is just the logistics, its was just a simple refactor to get rid of excessive blocks and code. Join all the var work together and thats it. Since you need a arr to return, once the looping is over, might as well do all your var work on one line first and luckily, three of the four vars have the same initialization. Remember, Javascript can optimize code when joining with , if possible. This also makes for smaller code as well.
PT
One more version of createNDimArray using map, apply and bind functions:
function createNDimArray(dims) {
return dims.length === 1
? new Array(dims[0])
: Array.apply(null, Array(dims[0])).map(createNDimensionalArray.bind(null, dims.slice(1)));
}
createNDimArray([3, 2, 5]); // returns 3x2x5 array
Creating an ND Array requires cloning nested ND arrays. Accordingly you will need a proper Array.prototype.clone() method and the rest is easy. To my knowledge the following is the simplest and most efficient way in JS.
Array.prototype.clone = function(){
return this.reduce((p,c,i) => (p[i] = Array.isArray(c) ? c.clone() : c, p),[])
}
function arrayND(...n){
return n.reduceRight((p,c) => c = (new Array(c)).fill().map(e => Array.isArray(p) ? p.clone() : p ));
}
var NDarr = arrayND(4,4,4,4,"."); // size of each dimension and the init value at the end
console.log(JSON.stringify(NDarr))
NDarr[0][1][2][3] = "kitty"; //access any location and change.
console.log(JSON.stringify(NDarr))
Reason For Anwser
There are good answers here but as JavaScript has changed here is an additional method of tackling this problem with some of the updated features in JavaScript.
function nArray (dem, size=dem, fill=null, currDepth=0) {
const arr = new Array(size).fill(fill);
return (currDepth+1 === dem) ? arr : arr.map(i => nArray(dem, size, fill, currDepth+1));
};
Notes
dem is the dimensions of the array.
size is the size of each dimension by default it is the dem value.
fill is the value that will be the default filled value.
currDepth is not to be used it is for the recursive nature of the function.
Create n dimensional matrix array with default values
function arr (arg, def = 0){
if (arg.length > 2){
return Array(arg[0]).fill().map(()=>arr(arg.slice(1)));
} else {
return Array(arg[0]).fill().map(()=>Array(arg[1]).fill(def));
}
}
//simple usage -> fills with 0
var s = arr([3,5,8,4]) // 4 dimensions
var t = arr([5,7]) // 2 dimensions
//fill with null
var k = arr([4,7,9] , null) // 3 dimensions
If you need to create 4d Array with index from 0 to 4 in the each cluster just do this code:
function createNDimArray(dimensions) {
if (dimensions.length > 0) {
var dim = dimensions[0];
var rest = dimensions.slice(1);
var newArray = new Array();
for (var i = 0; i < dim; i++) {
newArray[i] = createNDimArray(rest);
}
return newArray;
} else {
return undefined;
}
}
var MyArray=createNDimArray([5, 5, 5, 5]);
//returns a 5x5x5x5 array with index from 0 to 4;
MyArray[4][4][4][4]="MyArray 4d MyValue";
alert(MyArray[4][4][4][4]);
//For 5-demension array with this param.: 5x4x3x2x2 -> do this:
var MyArray_5d=createNDimArray([5, 4, 3, 2, 2]);
MyArray_5d[4][3][2][1][1]="MyArray 5d MyValue";
alert(MyArray_5d[4][3][2][1][1]);
MULTIDIMENSIONAL ARRAYS can be seen as EMBEDED ARRAYS.
See if the following can help.
<script type="text/javascript">"use strict";
const arr = [
["D1","D2","D3"],
[
["T11","T12","T13"],
["T21","T22","T23"]
]
];
for(let k=0;k<arr[0].length;k++)console.log(arr[0][k]);
// D1
// D2
// D3
for(let k=0;k<arr[1].length;k++)console.log(arr[1][k]);
// Array(3) [ "T11", "T12", "T13" ]
// Array(3) [ "T21", "T22", "T23" ]
for(let k=0;k<arr[1].length;k++)console.log(arr[1][0][k]);
// T11
// T12
for(let k=0;k<arr[1].length;k++)console.log(arr[1][1][k]);
// T21
// T22
for(let k=0;k<arr[1][0].length;k++)console.log(arr[1][0][k]);
// T11
// T12
// T13
for(let k=0;k<arr[1][1].length;k++)console.log(arr[1][1][k]);
// T21
// T22
// T23
</script>
// // // // // // // // // //
// // // // // // // // // //
And from the same point of vue, a MULTIDIMENSIONAL OBJECT !
<script type="text/javascript">"use strict";
const o = {
un:{u1:"U1",u2:"U2",u3:"U3"},
deux:{
trois : {d11:"D11",d12:"D12",d13:"D13"},
quatre: {t21:"T21",t22:"T22",t23:"T23"}
}
};
let ref = Object.keys(o);
for(let k=0;k<ref.length;k++)
console.log(ref[k] , ":" ,
Object.values(o)[k]);
// un : Object { u1: "U1", u2: "U2", u3: "U3" }
// deux : Object { trois: {…}, quatre: {…} }
// quatre: Object { t21: "T21", t22: "T22", t23: "T23" }
// trois : Object { d11: "D11", d12: "D12", d13: "D13" }
ref = Object.keys(o["un"]);
for(let k=0;k<ref.length;k++)
console.log(ref[k] , ":" ,
Object.values(o["un"])[k]);
// u1 : U1
// u2 : U2
// u3 : U3
ref = Object.keys(o["deux"]);
for(let k=0;k<ref.length;k++)
console.log(ref[k] , ":" ,
Object.values(o["deux"])[k]);
// trois : Object { d11: "D11", d12: "D12", d13: "D13" }
// quatre : Object { t21: "T21", t22: "T22", t23: "T23" }
ref = Object.keys(o["deux"]["trois"]);
for(let k=0;k<ref.length;k++)
console.log(ref[k] , ":" ,
Object.values(o["deux"]["trois"])[k]);
// d11 : D11
// d12 : D12
// d13 : D13
ref = Object.keys(o["deux"]["quatre"]);
for(let k=0;k<Object.keys(ref).length;k++)
console.log(ref[k] , ":" ,
Object.values(o["deux"]["quatre"])[k]);
// t21 : T21
// t22 : T22
// t23 : T23
ref = Object.keys(o["deux"]["trois"]);
console.log(ref[0] , ":" ,
Object.values(o["deux"]["trois"])[0]);
// d11 : D11
ref = Object.values(o["deux"]["quatre"]);
console.log(Object.keys(o["deux"]["quatre"])[ref.length-1] ,
":" , ref[ref.length-1] );
// t23 : T23
</script>

Categories

Resources