What's the first argument of Array.apply in JavaScript? - javascript

In an answer, #Tushar suggested the syntax corresponding to the following.
Array.apply(null, Array(3).fill(10))
.map(function (item, index) {
return item + index;
});
I do understand what's going on here and I'm satisfied. However, it bugs me a bit that there's this null valued argument doing seemingly nothing. So I went off and started researching. According to the wisdom on the web, it's a reference to this. Now, that gave me very little clarity and despite of putting in cucumbers, arrays, and objects into that, it didn't affect jack, as far I could tell. In fact, I'm curious why the following wouldn't be equivalent, let alone suffice.
Array(3).fill(10)
.map(function (item, index) {
return item + index;
});
Further on, I read something about Cr an IE not accepting array-like objects, which tells me even less. Also, it's a bit hard to verify the age of the article so the validity of its claim's hard to assess. I got as far as to the talk about prototype constructors and gave up, not being sure if I'm on the right path.

The first argument of apply is only important if you're using a function that uses this.
Running the following snippet should make it a little clearer:
var o = {value: 1};
var fn = function(a) {
console.log(a + this.value);
}
value = "something else";
fn("an argument ");
fn.apply(o, [20]);
// the above prints:
// an argument something else
// 21
https://jsfiddle.net/f2zw8edd/

Array(number) creates an array of the given size, however it does not populate any of its data points. Calling map or fill or pretty much anything else on it therefore won't do anything useful.
Array(item1, item2, item...) on the other hand creates an array with the given items as elements of the array. You can call map, fill, whatever you want on this and it will work.
So how can you use this to create an array of a given size that you can call map on? Exactly the way you see.
What Array.apply(null,Array(3)) does is create an unpopulated array of size 3, then passes those 3 undefined items as arguments to Array, thus resulting in
Array(undefined,undefined,undefined), which gives you a mappable array. It's important to note that you're creating two arrays here, but the Array(3) is discarded after use.
So why null? That would be because when creating an array, there is no place where this is relevant. Since the context is irrelevant, you can pass literally anything you want as the context and it will run just fine, but it's easiest to understand that context doesn't matter if you pass it null.

Related

Redefine array using itself

I wrote this solution to a problem I had, but I don't feel entirely comfortable with it. It seems to be dangerous, or at least in bad practice to use an array to redefine itself, sort of like trying to define a word but using the word in the definition. Can anyone explain either why this is wrong, or why it's ok?
let array = []
// other stuff happens to fill the array
array = array.filter(element => element !== true)
The reason I'm doing it this way is that I need that same variable name (array, for these purposes) to remain consistent throughout the function. The contents of the array might be added or removed multiple times before the array gets returned, depending on other behavior happening around it.
Let me know if this is too vague, I'll try to clarify. Thanks!
It's perfectly fine. The right side (array.filter(element => element !== true)) of the assignment will be evaluated first, generate a completely new array and only then it'll be assigned back into array.
You can loop through your array backwards and remove the items, as to not allocate another array and reassign.
for (i = array.length - 1; i >= 0; --i) {
if (!!array[i]) {
array.splice(i, 1);
}
}
This solution is:
Fast
Memory efficient
Will not interfere with any other code that has a reference to your original array.

How to iterate through a linked list in javascript

Someone shared this beautifully elegant way to create a linked list from an array.
function removeKFromList(l, k) {
let list = l.reduceRight((value, next)=>({next, value}), null);
console.log(list);
}
let l = [3, 1, 2, 3, 4, 5];
let k = 3;
removeKFromList(l, k);
It's pretty easy to iterate arrays (for, filter, map, reduce, etc.) but a linked list has none of those features. I need to iterate through to remove k values from the list. I'm guessing I need to create a variable for the current node and use a while loop, but there's no documentation on this. I've seen some repl code doing it but it seems unnecessarily complicated.
How do you iterate through a linked list in javascript?
First of all, although the idea of using reduce is indeed beautiful, I must say that the result is not so good, because the resulting nodes have a field "next" where the value is and a field "value" where the next is, i.e. they are swapped. So let's fix that:
function removeKFromList(l, k) {
let list = l.reduceRight((value, next)=>({value: next, next: value}), null);
console.log(list);
}
Secondly, the name of that function is awful, it should be named "arrayToLinkedList" or something more suggestive. Also, logging the result does not make sense, we should return it instead. Moreover, the parameter k is simply unused. Fixing those things:
function arrayToLinkedList(array) {
return array.reduceRight((prev, cur) => ({ value: cur, next: prev }), null);
}
Now, let's work on how to iterate over this. What do you think? I will give some hints because giving the answer directly probably won't help you learn much:
Observe that the linked list itself is:
Either the value null, or
A plain object with two fields, one field "value" which can be anything, and one field "next" which is a linked list.
Observe that the above (recursive) definition is well-defined and covers all cases.
Now, use that to your assistence. How do you get the first value? That would be simply myList.value, right? Now how do I get the second value? By applying .next, we get the next linked list, and so on. If next is null, you know you have to stop.
Let me know if you need further assistance.
EDIT: I noticed that you're looking for the right way to make an iterating method on lists with an idea of "adding it to the prototype" or something. So, about that:
To add an instance method by a prototype, you'd need your linked lists to be a class. That would be overcomplicating, unless you really have a good reason to define a class for this (which would be creating several methods and utility things around it).
It is much better, in my opinion, to just define a function that takes one linked list as the first parameter and a callback function as the second parameter, similarly to lodash's .each:
function forEachValueInLinkedList(linkedList, callback) {
// here you loop through all values and call
// callback(value)
// for each value.
}
I'd say this would be the most "javascriptonic" way to do it.

Array type being picked up as array value

I have some simple Javascript looping through an array of items (Tridion User Groups) to check if the user is a member of a specific group.
I can easily code around the issue shown below ( see && extensionGroup !== 'true') but I want to understand why the isArray = true is counted as a value in the array - any ideas?
The screenshot below demonstrates that the value extensionGroups has been set thus
var extensionGroups = ["NotEvenARealGroup", "Author", "ExampleGroupAfterOneUserIsActuallyIn"];
but returns the isArray value as a 4th value?
updated to show images a little clearer
You're using for in to iterate an array; don't do that. Use for (or forEach):
for(var i = 0; i < extensionGroups.length; i++) {
var extensionGroup = extensionGroups[i];
// ...
}
The reason this fails is because for in is used to iterate over an object's properties in JavaScript. Iterating over an array in this way means you get anything else assigned to it, such as this property or length.
And if you're able to use Array#forEach, it's probably most appropriate here:
extensionGroups.forEach(function(extensionGroup) {
// ...
});
For..in, technically speaking, doesn't iterate through values. It iterates through property names. In an array, the values ARE properties, under the hood. So when you iterate over them with for..in you get funky stuff like that happening.
Which highlights my next point: don't use for..in. Don't use it for arrays -- don't use it for anything, really. Ok -- maybe that's going a bit too far. How about this: if you feel the need to use for..in, think hard to see if it's justifiable before you do it.

Javascript array becomes an object structure

I'm experiencing an odd behavior (maybe it isn't odd at all but just me not understanding why) with an javascript array containing some objects.
Since I'm no javascript pro, there might very well be clear explanation as to why this is happening, I just don't know it.
I have javascript that is running in a document. It makes an array of objects similar to this:
var myArray = [{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...];
If I print out this array at the place it was created like JSON.stringify(myArray), I get what I was expecting:
[{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...]
However, if I try to access this array from a child document to this document (a document in a window opened by the first document) the array isn't an array any more.
So doing JSON.stringify(parent.opener.myArray) in the child document will result in the following:
{"0":{"Id":"guid1","Name":"name1"},"1":{"Id":"guid2","Name":"name2"},...}
And this was not what I was expecting - I was expecting to get the same as I did in teh parent document.
Can anyone explain to me why this is happening and how to fix it so that the array is still an array when addressed from a child window/document?
PS. the objects aren't added to the array as stated above, they are added like this:
function objTemp()
{
this.Id = '';
this.Name = '';
};
var myArray = [];
var obj = new ObjTemp();
obj.Id = 'guid1';
obj.Name = 'name1';
myArray[myArray.length] = obj;
If that makes any difference.
Any help would be much appreciated, both for fixing my problem but also for better understanding what is going on :)
The very last line might be causing the problem, have you tried replacing myArray[myArray.length] = obj; with myArray.push(obj);? Could be that, since you're creating a new index explicitly, the Array is turned into an object... though I'm just guessing here. Could you add the code used by the child document that retrieves myArray ?
Edit
Ignore the above, since it won't make any difference. Though, without wanting to boast, I was thinking along the right lines. My idea was that, by only using proprietary array methods, the interpreter would see that as clues as to the type of myArray. The thing is: myArray is an array, as far as the parent document is concerned, but since you're passing the Array from one document to another, here's what happens:
An array is an object, complete with it's own prototype and methods. By passing it to another document, you're passing the entire Array object (value and prototype) as one object to the child document. In passing the variable between documents, you're effectively creating a copy of the variable (the only time JavaScript copies the values of a var). Since an array is an object, all of its properties (and prototype methods/properties) are copied to a 'nameless' instance of the Object object. Something along the lines of var copy = new Object(toCopy.constructor(toCopy.valueOf())); is happening... the easiest way around this, IMO, is to stringency the array withing the parent context, because there, the interpreter knows it's an array:
//parent document
function getTheArray(){ return JSON.stringify(myArray);}
//child document:
myArray = JSON.parse(parent.getTheArray());
In this example, the var is stringified in the context that still treats myArray as a true JavaScript array, so the resulting string will be what you'd expect. In passing the JSON encoded string from one document to another, it will remain unchanged and therefore the JSON.parse() will give you an exact copy of the myArray variable.
Note that this is just another wild stab in the dark, but I have given it a bit more thought, now. If I'm wrong about this, feel free to correct me... I'm always happy to learn. If this turns out to be true, let me know, too, as this will undoubtedly prove a pitfall for me sooner or later
Check out the end of this article http://www.karmagination.com/blog/2009/07/29/javascript-kung-fu-object-array-and-literals/ for an example of this behavior and explanation.
Basically it comes down to Array being a native type and each frame having its own set of natives and variables.
From the article:
// in parent window
var a = [];
var b = {};
//inside the iframe
console.log(parent.window.a); // returns array
console.log(parent.window.b); // returns object
alert(parent.window.a instanceof Array); // false
alert(parent.window.b instanceof Object); // false
alert(parent.window.a.constructor === Array); // false
alert(parent.window.b.constructor === Object); // false
Your call to JSON.stringify actually executes the following check (from the json.js source), which seems to be failing to specify it as an Array:
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
//stringify

Memory assignment to arrays

I was wondering if any one knows how memory is handled with JS arrays if you have an array that starts with a high value.
For example, if you have:
array[5000] = 1;
As the first value in the array, everything before 5000 simply does not exist, will the amount of memory assigned to the array cater for the unassigned 4999 positions prior to it... or will it only assign memory to the value in the array for [5000] ?
I'm trying to cut down on the amount of memory used for my script so this led to me wondering about this question :)
When assigning a value to the 5000th key, not the whole array is populated:
var array = []; // Create array
array[5000] = 1;
'1' in array; // false: The key does not exists
Object.keys(array); // 5000 (it's the only key)
If you want to blow your new browser with arrays, populate a typed array:
var array = new ArrayBuffer(6e9); // 6 Gigs
Both can be verified easily in Chrome: Open the console and memory console (Shift+Esc), and paste the code. window.a=new Array(6e9); or window.a=[];window[6e9]=1; doesn't result in a significant memory increase,
while window.a=new ArrayBuffer(6e9); crashes the page.
PS. 6e9 === 6000000000
Javascript is really interpreted and run by the browser, so it depends on how the browser implements this behavior. In theory, once you do array[5000], you have an array of 5001 elements, all except the 5001st being undefined.
Though if I were the one implementing the logic for running such script, undefined would be the default value if not assigned to anything else, meaning I could probably get away with defining a map with 1 entry assigning key 5000 to value 1. Any accesses to any other value in the array would automatically return undefined, without having to do unnecessary work.
Here's a test of this here. As you can see, the alert is seen immediately.
JS arrays are actually not arrays as you know them from other programming languages like C, C++, etc. They are instead objects with a array like way of accessing them. This means that when you define array[5000] = 1; You actually define the 5000 property of the array object.
If you had used a string as the array key you would have been able to access the index as a property as well to demonstrate this behavior, but since variable names can't start with a number array.5000 would be invalid.
array['key'] = 1;
alert( array.key ); // Gives you 1
This means that arrays will probably be implemented much like objects, although each implementation is free to optimize, thus giving you the behavior you except from objects where you can define object.a and object.z without defining the whole alphabet.

Categories