How to write 'for-in' in JavaScript ? [duplicate] - javascript

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Functional approach to basic array construction
I am new to js. I just want to know which one is the right approach. Below I have posted my code.
var doubles = (i*20 for (i in it));
var doubles ={i*20 for (i in it)};
Thanks

You should use ordinary for loops when iterating over arrays. for...in is used for iterating over object properties.
So, the correct way is:
for (var i = 0; i < array.length; ++i) {
// do something
}
To iterate over object properties:
for (var prop in obj) {
// do something with prop
}

Assuming it is an array, you can use .map():
var doubles = it.map(function(i){ return i*20; });
Also you might want to have a look at how to write List/Array comprehensions in JavaScript
Assuming you want to use Mozilla's Generator expressions (where it is an existing Iterator), you need to use square brackets:
var twentyfolds = [i*20 for (i in it)];

For future reference, ECMAScript 6 (aka Harmony) will most likely introduce a new sane way of iterating over objects (arrays included):
for(var x of array) {
// do something with x
}
It will also introduce array comprehensions and generator expressions to the core language:
var arr = [1, 2, 3];
console.log([i*20 for (i of arr)])

Both of these options are syntax errors. If 'it' is a list, then you can iterate through it with a for loop or the forEach method. In your case however, it looks like you are really looking for the map method.
var doubles = it.map(function (i) { return i * 20; });

Related

Accessing a specific array

I have a set of arrays named in the following fashion: question0, question1, question2 etc.
I also have a variable that corresponds to the question number.
With this, how would I access array question0 without having to type out the whole name. For example, in a for loop, I have if the loop number is 5, I want to do an operation on question5. I know this is really simple, but I haven't been able to find the solution.
Variables ending in sequential numbers are usually a hint to use arrays. Sounds like you need an array of arrays instead, and then you can just:
doOperation(question[somenumber])
Why not just use a big array, question, where each item it itself an array?
That aside, if the variables are global then you could use window['question'+i], with i being the number of the question.
Don't use variable names with an ordinal number as their suffix; use the proper structures, such as an array:
var questions = ['why?', 'what?', 'where?'],
nr = 2; // the question number
alert(questions[nr]);
In my example, each "question" is a string, but it could be any other structure, including another array.
I think it would be simpler to have your question(n) values as an array. From there you could use the following: questions[i] where i represents the current loop number, thus improving manipulation.
var questions = [["What?", "Why?"], ["When?", "How?"]];
for(var i = 0; i < questions.length; i++) {
for(var j = 0; j < questions[i].length; j++) {
console.info(questions[i][j]);
}
}

Create copy of multi-dimensional array, not reference - JavaScript

This is also referred to as "deep copying", which I've found some articles on. Closest seems to be this one but it's for jQuery - I'm trying to do this without a library.
I've also seen, in two places, that it's possible to do something like:
arr2 = JSON.decode(JSON.encode(arr1));
But that's apparently inefficient. It's also possible to loop and copy each value individually, and recurs through all the arrays. That seems tiring and inefficient as well.
So what's the most efficient, non-library way to copy a JavaScript multi-dimensional array [[a],[b],[c]]? I am completely happy with a "non-IE" method if necessary.
Thanks!
Since it sounds like you're dealing with an Array of Arrays to some unknown level of depth, but you only need to deal with them at one level deep at any given time, then it's going to be simple and fast to use .slice().
var newArray = [];
for (var i = 0; i < currentArray.length; i++)
newArray[i] = currentArray[i].slice();
Or using .map() instead of the for loop:
var newArray = currentArray.map(function(arr) {
return arr.slice();
});
So this iterates the current Array, and builds a new Array of shallow copies of the nested Arrays. Then when you go to the next level of depth, you'd do the same thing.
Of course if there's a mixture of Arrays and other data, you'll want to test what it is before you slice.
I'm not sure how much better JSON.stringify and JSON.parse than encode and decode, but you could try:
JSON.parse(JSON.stringify(array));
Something else I found (although I'd modify it a little):
http://www.xenoveritas.org/blog/xeno/the-correct-way-to-clone-javascript-arrays
function deepCopy(obj) {
if (typeof obj == 'object') {
if (isArray(obj)) {
var l = obj.length;
var r = new Array(l);
for (var i = 0; i < l; i++) {
r[i] = deepCopy(obj[i]);
}
return r;
} else {
var r = {};
r.prototype = obj.prototype;
for (var k in obj) {
r[k] = deepCopy(obj[k]);
}
return r;
}
}
return obj;
}
As you asked for performance, I guess you also would go with a non-generic solution. To copy a multi-dimensional array with a known number of levels, you should go with the easiest solution, some nested for-loops. For your two-dimensional array, it simply would look like this:
var len = arr.length,
copy = new Array(len); // boost in Safari
for (var i=0; i<len; ++i)
copy[i] = arr[i].slice(0);
To extend to higher-dimensional arrays, either use recursion or nested for loops!
The native slice method is more efficient than a custom for loop, yet it does not create deep copies, so we can use it only at the lowest level.
Any recursive algorithm that doesn't visit the same node twice will be about as efficient as you get with javascript (at least in a browser) - in certain situations in other languages you might get away with copying chucks of memory, but javascript obviously doesn't have that ability.
I'd suggest finding someone who's already done it and using their implementation to make sure you get it right - it only needs to be defined once.

Adding functions to javascript's Array class breaks for loops

I was looking for a way to add max/min functions to JavaScript's Array class, which seemed to be a solved problem: JavaScript: min & max Array values?. However, when I tried using that, I started getting errors from my code. It turns out that this approach doesn't work with loops.
for(i in myArray) { console.log(i) }
prints out
1
2
3
max
min
Is there another approach I can use?
The accepted solution solves your immediate problem, but extending core objects is generally a bad idea. What happens when you include a library later that uses for..in? Or when you forget months later and use the wrong approach in a different section of your code?
Another option is to wrap and extend. Create a new type that uses an instance of Array as its prototype:
function ArrayThing() {}
ArrayThing.prototype = new Array();
Now you've got an object that you can extend without affecting Array:
ArrayThing.prototype.max = function() {
return Math.max.apply(null, [].slice.call(this, 0))
}
ArrayThing.prototype.min = function() {
return Math.min.apply(null, [].slice.call(this, 0))
}
var list = new ArrayThing();
// standard array methods still work
list.push(5);
list.push(22);
list.push(0);
list.push(-14);
list.length // => 4
// as do your new custom methods
list.max() // => 22
list.min() // => -14
This won't work in every situation, but unless you're sure you really, really need an Array, this is a useful alternative.
The for...in loop is used for looping through properties of an object. If you want to get the values from your array, you can do this:
for (var i = 0; i < myArray.length; i++)
{
console.log(myArray[i])
}
for in is a common Javascript trap. Instead of behaving like a foreach from other languages, it actualy enumerates all properties in a given object.
Since Javascript Arrays happen to have a property for each index using for in works sometimes, but as you have seen, it also enumerates any other properties you add. Another issue is that the for in is not guaranteed to go through the properties in any particular order, so your results can vary depending on which browser/runtime you use.
It is safer, then, to just use a boring for loop instead. There are many for loop idioms in Javascript, so I will list some:
Regular for loop:
for(i=0; i<arr.length; i++){
Regular loop, caching the length:
for(i=0, n=arr.length; i<n; i++){
Loops over arrays of objects/NodeLists:
for(i=0; obj=arr[i]; i++){ //this works as long as the array has no falsy values
foo(obj)
You need to apply a check using hasOwnProperty.
However this needs to applied wherever you are looping.
i.e:
for(i in myArray)
{
if(arr.hasOwnProperty(i))
{
console.log(i);
}
}
There's a more modern (IE9+) way to do this now:
var g = [];
Object.defineProperty(g, "log", {
enumerable: false,
configurable: false,
writable: false,
value: function(){ console.log(this); }
});
g.push(5);
g.push(9);
var t;
for (t in g){
console.log(g[t]);
}
prints 5 and 9 but does not list the "log" function
g.log() -> echos [5,9]
The key to this working is being able to flag a property as "enumerable: false" with marks the property as something that shouldn't be iterated over.
More on Object.defineProperty here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

A better way to splice an array into an array in javascript

Is there a better way than this to splice an array into another array in javascript
var string = 'theArray.splice('+start+', '+number+',"'+newItemsArray.join('","')+'");';
eval(string);
You can use apply to avoid eval:
var args = [start, number].concat(newItemsArray);
Array.prototype.splice.apply(theArray, args);
The apply function is used to call another function, with a given context and arguments, provided as an array, for example:
If we call:
var nums = [1,2,3,4];
Math.min.apply(Math, nums);
The apply function will execute:
Math.min(1,2,3,4);
UPDATE: ES6 version
If you're coding in ES6, you can use the "spread operator" (...).
array.splice(index, 0, ...arrayToInsert);
To learn more about the spread operator see the MDN documentation.
The 'old' ES5 way
If you wrap the top answer into a function you get this:
function insertArrayAt(array, index, arrayToInsert) {
Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));
}
You would use it like this:
var arr = ["A", "B", "C"];
insertArrayAt(arr, 1, ["x", "y", "z"]);
alert(JSON.stringify(arr)); // output: A, x, y, z, B, C
You can check it out in this jsFiddle: http://jsfiddle.net/luisperezphd/Wc8aS/
This question is really old, but with ES6, there's a simpler way to do this using the spread operator:
sourceArray.splice(index, 0, ...insertedArray)
If you're using uncompiled javascript in the browser, be sure to check if it's supported in your target browser at https://kangax.github.io/compat-table/es6/#test-spread_(...)_operator.
Also, this may be slightly off topic, but if you don't want or need to modify the original array, but could use a new array instead, consider this approach:
mergedArray = sourceArray.slice(0, index).concat(insertedArray, sourceArray.slice(index))
You can also add such a function to the Array prototype, if you want something that is almost identical to the splice method. E.g.
Array.prototype.spliceArray = function(index, n, array) {
return Array.prototype.splice.apply(this, [index, n].concat(array));
}
Then usage would simply be:
var array = ["A","B","C","","E","F"];
array.splice(3,1,"D");
// array is ["A","B","C","D","E","F"]
array.spliceArray(3,3,["1","2","3"]);
// array is ["A","B","C","1","2","3"]
See it in action here: http://jsfiddle.net/TheMadDeveloper/knv2f8bb/1/
Some notes:
The splice function modifies the array directly, but returns the an array of elements that were removed... not the spliced array.
While it's normally not recommended to extend core javascript classes, this is relatively benign with most standard frameworks.
Extending Array won't work in cases where specialized array classes are used, such as an ImageData data Uint8ClampedArray.
The answers above that involve splice.apply and insert the array in a one liner will blow up the stack in a stack overflow for large array.
See example here:
http://jsfiddle.net/gkohen/u49ku99q/
You might have to slice and and push each item of the inserted and remaining part of the original array for it to work.
See fiddle: http://jsfiddle.net/gkohen/g9abppgy/26/
Array.prototype.spliceArray = function(index, insertedArray) {
var postArray = this.splice(index);
inPlacePush(this, insertedArray);
inPlacePush(this, postArray);
function inPlacePush(targetArray, pushedArray) {
// Not using forEach for browser compatability
var pushedArrayLength = pushedArray.length;
for (var index = 0; index < pushedArrayLength; index++) {
targetArray.push(pushedArray[index]);
}
}
}
There are a lot of clever answers here, but the reason you use splice is so that it puts the elements into the current array without creating another. If you have to create an array to concat() against so you can use apply() then you're creating 2 additional trash arrays! Sorta defeats the whole purpose of writing esoteric Javascript. Besides if you don't care about that memory usage stuff (and you should) just dest = src1.concat(src2); it is infinitely more readable. So here's is my smallest number of lines while staying efficient answer.
for( let item of src ) dest.push( item );
Or if you'd like to polyfill it and have a little better browser support back:
src.forEach( function( x ) { dest.push(x); });
I'm sure the first is more performant (it's a word ;), but not supported in all browsers out there in the wild.
If you don't want to concatenate inserting items to first two parameters of Array.splice(),
an elegant way is to use Function.bind() and Function.apply() together.
theArray.splice.bind(null, startIndex, deleteCount).apply(newItemsArray);
I wanted to have a function which would take only part of the source array so I have mine slightly different
based off CMS's answer
function spliceArray(array, index, howmany, source, start, end) {
var arguments;
if( source !== undefined ){
arguments = source.slice(start, end);
arguments.splice(0,0, index, howmany);
} else{
arguments = [index, howmany];
}
return Array.prototype.splice.apply(array, arguments)
}
Array.prototype.spliceArray = function(index, howmany, source, start, end) {
return spliceArray(this, index, howmany, source, start, end);
}
You can see it at: https://jsfiddle.net/matthewvukomanovic/nx858uz5/

Most efficient way to convert an HTMLCollection to an Array

Is there a more efficient way to convert an HTMLCollection to an Array, other than iterating through the contents of said collection and manually pushing each item into an array?
var arr = Array.prototype.slice.call( htmlCollection )
will have the same effect using "native" code.
Edit
Since this gets a lot of views, note (per #oriol's comment) that the following more concise expression is effectively equivalent:
var arr = [].slice.call(htmlCollection);
But note per #JussiR's comment, that unlike the "verbose" form, it does create an empty, unused, and indeed unusable array instance in the process. What compilers do about this is outside the programmer's ken.
Edit
Since ECMAScript 2015 (ES 6) there is also Array.from:
var arr = Array.from(htmlCollection);
Edit
ECMAScript 2015 also provides the spread operator, which is functionally equivalent to Array.from (although note that Array.from supports a mapping function as the second argument).
var arr = [...htmlCollection];
I've confirmed that both of the above work on NodeList.
A performance comparison for the mentioned methods: http://jsben.ch/h2IFA
not sure if this is the most efficient, but a concise ES6 syntax might be:
let arry = [...htmlCollection]
Edit: Another one, from Chris_F comment:
let arry = Array.from(htmlCollection)
I saw a more concise method of getting Array.prototype methods in general that works just as well. Converting an HTMLCollection object into an Array object is demonstrated below:
[].slice.call( yourHTMLCollectionObject );
And, as mentioned in the comments, for old browsers such as IE7 and earlier, you simply have to use a compatibility function, like:
function toArray(x) {
for(var i = 0, a = []; i < x.length; i++)
a.push(x[i]);
return a
}
I know this is an old question, but I felt the accepted answer was a little incomplete; so I thought I'd throw this out there FWIW.
For a cross browser implementation I'd sugguest you look at prototype.js $A function
copyed from 1.6.1:
function $A(iterable) {
if (!iterable) return [];
if ('toArray' in Object(iterable)) return iterable.toArray();
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
}
It doesn't use Array.prototype.slice probably because it isn't available on every browser. I'm afraid the performance is pretty bad as there a the fall back is a javascript loop over the iterable.
This works in all browsers including earlier IE versions.
var arr = [];
[].push.apply(arr, htmlCollection);
Since jsperf is still down at the moment, here is a jsfiddle that compares the performance of different methods. https://jsfiddle.net/qw9qf48j/
To convert array-like to array in efficient way we can make use of the jQuery makeArray :
makeArray: Convert an array-like object into a true JavaScript array.
Usage:
var domArray = jQuery.makeArray(htmlCollection);
A little extra:
If you do not want to keep reference to the array object (most of the time HTMLCollections are dynamically changes so its better to copy them into another array, This example pay close attention to performance:
var domDataLength = domData.length //Better performance, no need to calculate every iteration the domArray length
var resultArray = new Array(domDataLength) // Since we know the length its improves the performance to declare the result array from the beginning.
for (var i = 0 ; i < domDataLength ; i++) {
resultArray[i] = domArray[i]; //Since we already declared the resultArray we can not make use of the more expensive push method.
}
What is array-like?
HTMLCollection is an "array-like" object, the array-like objects are similar to array's object but missing a lot of its functionally definition:
Array-like objects look like arrays. They have various numbered
elements and a length property. But that’s where the similarity stops.
Array-like objects do not have any of Array’s functions, and for-in
loops don’t even work!
This is my personal solution, based on the information here (this thread):
var Divs = new Array();
var Elemns = document.getElementsByClassName("divisao");
try {
Divs = Elemns.prototype.slice.call(Elemns);
} catch(e) {
Divs = $A(Elemns);
}
Where $A was described by Gareth Davis in his post:
function $A(iterable) {
if (!iterable) return [];
if ('toArray' in Object(iterable)) return iterable.toArray();
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
}
If browser supports the best way, ok, otherwise will use the cross browser.
I suppose that calling Array.prototype functions on instances of HTMLCollection is a much better option than converting collections to arrays (e.g.,[...collection] or Array.from(collection)), because in the latter case a collection is unnecessarily implicitly iterated and a new array object is created, and this eats up additional resources. Array.prototype iterating functions can be safely called upon objects with consecutive numeric keys starting from [0] and a length property with a valid number value of such keys' quantity (including, e.g., instances of HTMLCollection and FileList), so it's a reliable way. Also, if there is a frequent need in such operations, an empty array [] can be used for quick access to Array.prototype functions; or a shortcut for Array.prototype can be created instead. A runnable example:
const _ = Array.prototype;
const collection = document.getElementById('ol').children;
alert(_.reduce.call(collection, (acc, { textContent }, i) => {
return acc += `${i+1}) ${textContent}` + '\n';
}, ''));
<ol id="ol">
<li>foo</li>
<li>bar</li>
<li>bat</li>
<li>baz</li>
</ol>
Sometimes, Even You have written code the correct way, But still it doesn't work properly.
var allbuttons = document.getElementsByTagName("button");
console.log(allbuttons);
var copyAllButtons = [];
for (let i = 0; i < allbuttons.length; i++) {
copyAllButtons.push(allbuttons[i]);
}
console.log(copyAllButtons);
you get empty array.
Like, This
HTMLCollection []
[]
Console_javascript
For Solving this problem, You have to add link of javascript file after body tag in html file.
<script src="./script.js"></script>
As you can see below,
html_file
Final Output
HTMLCollection(6) [button.btn.btn-dark.click-me, button.btn.btn-dark.reset, button#b, button#b, button#b, button#b, b: button#b]
(6) [button.btn.btn-dark.click-me, button.btn.btn-dark.reset, button#b, button#b, button#b, button#b]

Categories

Resources