JavaScript the Good Parts: unshift function - javascript

I'm reading JavaScript:The Good Parts by Crockford and I'm having trouble understanding the reimplementation of the unshift method that he does in his book.Here is the the code:
Array.method('unshift', function ( ) {
this.splice.apply(this,
[0, 0].concat(Array.prototype.slice.apply(arguments)));
return this.length;
});
It would be useful if someone could go through what is happening step by step. One of the things that I don't understand is why he concatenates [0 , 0] to the result of the Array.prototype.slice.

why he concatenates [0 , 0] to the result of the Array.prototype.slice
The first two arguments of splice (which is where the resulting array is applied to) are:
index which is 0 because you are adding to the front of the array
howMany (to remove) which is 0 because you are just adding new elements
The remaining arguments are the values to be added to the front of the array, which are taken from Array.prototype.slice.apply(arguments) (to convert the data into the arguments object into data in an array).

Related

Please explain the recursive solution of finding the depth of an array

I found solution on this link.
Get array's depth in JavaScript
Due to inadequate reputation, I could not comment to ask for explanation through comments.
function getArrayDepth(value) {
return Array.isArray(value) ?
1 + Math.max(...value.map(getArrayDepth)) :
0;
}
let testRy = [1,2,[3,4,[5,6],7,[8,[9,91]],10],11,12]
console.log(getArrayDepth(testRy))
Could you please explain why you are adding 1 and why you are using the Math.max function and the spread operator in the function above? How does this function work?
First, a primer on recursion:
Recursion ultimately tries to solve the most basic form of the problem you can have and then gradually narrow down any complex problem to the most basic form. So you need the following (probably doesn't make complete sense until you read all of it):
You need to solve the base case.
the base case also serves as a terminal condition. You want to stop calling the function recursively at some point. Once you reach the base case you don't need to recurse any more.
You need a reduction step. You start with a big problem and you aim to go to the base form of it and solve it (the base case from 1.). If the current form is not the base, then it's not solvable - you need to reduce the problem a bit and call the function recursively.
So, in this case, the base case is that you get a value that is not an array. Since it's not an array, it doesn't have depth, hence you return zero. SOLVED! That is it.
However, what happens if you do get an array? Well, any array will have some depth. So, you can count one then get the contents of the array. That's the reduction step - you've reduced it from "I have an array of unknown depth" to "I have the contents of my array". When you recursively call getArrayDepth with the contents, you will re-evaluate whether you have an array or not and count appropriately. If you sum all the times you've had an array with depth of at least 1, you get the depth.
So far, we can solve the following things
Input: 42
Output (depth): 0
Why?: It's the base case - it's not an array, so we return 0 and don't recursively call the function.
Input: [42]
Output (depth): 1
Why?: We have an array ->
   count depth of 1 add the result of the recursive call with the contents 42 -->
   it's the base case - it's not an array, so we return 0 and don't recursively call the function.
Going back, we've had 0 and 1 which totals 1.
Input: [[42]]
Output (depth): 2
Why?: We have an array ->
   count depth of 1 and recursively call with the contents [42] -->
   we have an array -->
       count depth of 1 and recursively call with the contents 42 --->
       it's the base case - it's not an array, so we return 0 and don't recursively call the function.
Going back, we've had 0, 1 and 1 which totals 2.
And so on.
Now, as for why you use Math.max and Array#map. Since arrays can have many elements, you use .map to call getArrayDepth on each of them. This will cause further recursion to work it out but at the end you will get an array of all the depths of the elements, so ["a", ["b"], [["c"]]] will be transformed into [1, 2, 3]. Since you need to get the total depth, you need the highest number - this is achieved by using Math.max with spread syntax to return the maximum value.
The individual values in testRy are:
1
2
[3,4,[5,6],7,[8,[9,91]],10]
11
12
On first execution of getArrayDepth with testRy, Array.isArray(value) will return true. So because this is true, we know we have at least depth 1 so that is why the 1+ is there. It then adds that to the the maximum value of calling getArrayDepth on each element in testRy. So that would look like:
getArrayDepth(1) => Array.isArray() false => 0
getArrayDepth(2) => Array.isArray() false => 0
getArrayDepth([3,4,[5,6],7,[8,[9,91]],10]) => Array.isArray() true => 1 + next max
so we hit another array, now we have at least another depth but we need to check if there are arrays in this array, so the cycle continues with this new array (it still has to finish testRy, but with the way it executes, it will go through this one first):
getArrayDepth(3) => Array.isArray() false => 0
getArrayDepth(4) => Array.isArray() false => 0
getArrayDepth([5,6]) => Array.isArray() true => 1 + next max
and this repeats until the end of testRy.
Translating the code to English would sound something like this:
Is the input an array?
yes - get the largest (Math.max) depth of each of the elements (value.map(getArrayDepth)) and increment it by 1 (1 +) to account for the current input array.
no - there is no array, thus no depth, return 0.
The spread operator is used to supply the array return value of value.map(getArrayDepth) as separate arguments. Since that is what Math.max expects.

can somebody explain this JS code

myArray = [];
myArray.length;
myArray[1000000] = true;
myArray.length;
why does this return 100001?
ive read this in douglas crockford book and i dont understand what this
means
1000001, you've probably meant. An array is extended as soon as you access its element beyond current bounds. That is why after you touch its element no. 10⁶, it becomes of 10⁶ + 1 elements.
Maybe you check with a smaller index, like 10 and have a look to the values of the array.
The result is a sparse array with missing indices and one value on index 10.
The length is, as always, last index plus one.
var myArray = [];
console.log(myArray.length);
myArray[10] = true;
console.log(myArray.length);
console.log(myArray);
It returns 100001 because,
myArray = []; creates an empty array
That's why myArray.length; = 0
When you do myArray[1000000] = true;
that time actually what happens is it puts all values from 0th index to 999999th index to undefined and then puts you 1000000 value to true. so actually there are 1000001 values in an array.
That's why the length is 1000001
every function of array specifies that array works as a collection of things in a sequence which means you cant put value at random place even if you provide any random index the by the default value (undefined) will be available as a value for other places.
Arrays start at index 0, so if we have an array:
var arr = [1,2,3,4,5];
arr[0] is 1, arr[1] is 2 and so on, and the array's length is 5, because it contains 5 "elements". So, arrays start at index 0 (if you wanted to get the first element of the array, you would have to run arr[0], not arr[1], because arr[1] would be the second element) but .length counts the elements and starts with "1" for the first element, "2" for the second element, etc.
Now when you run this:
var arr = [1,2,3,4,5];
arr[200] = "whatever";
console.log(arr);
You will get this output:
(201) [1, 2, 3, 4, 5, undefined × 195, "whatever"]
So when we randomly added an element to the array at index 200 (which is actually the 201th element in the array, since arrays start at 0, not 1), the array's length just grew from 5 to 201 (the same thing can be seen in the output), and as you can see in the output, the array now contains the original 5 elements (1, 2, 3, 4 and 5), 195 ghost elements whose value undefined when you query them (don't bother what I mean by ghost element just now though), and 1 "whatever" element at its end (the output in the console would be slightly different if you manually inserted the 195 undefined elements, but don't bother yourself with such things for now).
So in your case, an array is defined (myArray), by default, it contains nothing, so it has a length of 0. Then you go on and say "I want to add an element at the 1000000th index of this array" (the line myArray[1000000] = true), don't let the true value of the added element confuse you! the output would be the same regardless of what followed the myArray[1000000] = part, so myArray[1000000] = "whatever" would give you the same output: an array whose length is 1000001. Here is the output of your array when used with console.log(..) (remember, the output would be the same even if "whatever" was inserted in place of true):
(1000001) [undefined × 1000000, true]
Also, remember, the array you get when you say var arr = []; arr[1000] = "foo" is sparse (If you are interested in sparse arrays)
The other comments have answered your question correctly as well but I decided to do so because: 1. I have a bad feeling the true part in myArray[1000000] = true is confusing you. 2. They explained the arrays-start-at-0 thing in a not-too-clear way.

Indexing an array from the end

I am learning Javascript and I come from a Python background.
So, it's fairly intuitive to me to try and index an array from the end i.e using negative indices.
From what I have read so far, Javascript doesn't support them.
However, I found something which seems interesting but I am unable to understand the reason behind this.
todos = ['item1', 'item2', 'item3']
function updateTodos(index, new_value) {
todos[index] = new_value
console.log(todos)
}
function deleteTodos(index) {
todos.splice(index)
console.log(todos)
}
deleteTodos(-1)
updateTodos(-1, 'new_item')
deleteTodos(-1)
Output
["item1", "item2"]
["item1", "item2", -1: "new_item"]
["item1", -1: "new_item"]
Q: Why is deleteTodos able to delete the correct by index while updateTodos isn't?
Q: How can I accommodate for this behavior of negative indexing in updateTodos and any function dealing with the array data structure in general?
As far as I can make out, the indexing in updateTodos looks for the index variable and update the value at that index, if it exists, else, it creates a key-value pair. The splice method supports negative indexing, doesn't it?
I would appreciate if you can clarify my reasoning and/or help me with useful resources to understand this concept better.
According to MDN, splice does support negative indexing.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
Index at which to start changing the array (with origin 0). If greater
than the length of the array, actual starting index will be set to the
length of the array. If negative, will begin that many elements from
the end of the array (with origin 1).
So, that's why deleting works.
To enable negative indexing in update, you could check if the supplied argument is negative. If it is, it is a simple manner of using array.length + index to have the python-like indexing.
2 things to add to the accepted answer:
1) Remember that when using splice that if you don't specify an amount of items to delete (second argument) then everything from the index you specify to the end of the array will be deleted. From your question I doubt this is what you want.
2) Splice can also be used to add elements to an array (third argument, fourth argument etc.) so would work fine for your update function.
A complete example to fix both issues would be like this:
function updateTodos(index, new_value) {
todos.splice(index, 1, new_value);
}
function deleteTodos(index) {
todos.splice(index, 1);
}
You could get even fancier and combine them both into one function where if you specify a value then that gets updated else it gets deleted, but it's probably unnecessary.

Code to get last 2 items from an array

Am trying to get last 2 items from an array
result.forEach(function (re) {
console.log(re.files)
// prints ["utilities.rb", "print_utilities.rb", "lities.rb", "agination.rb"]
});
i only want to last two elements in the array in the order
[ "agination.rb", "print_utilities.rb"]
How it is possible
Use the Array.prototype.slice method for this with a negative index.
From MDN:
As a negative index, begin indicates an offset from the end of the
sequence. slice(-2) extracts the last two elements in the sequence.
slice returns a new array, so you can store the result in a new variable.
So in your case, simply use:
var arrayWithLast2Items = re.files.slice(-2);
jquery provides a slice method for this
e.g
$([1,2,3]).slice(-2)
https://api.jquery.com/slice/
Have a temp array with size 2, iterate the elements of original array and over write/copy elements to the temp array.
Once iteration is complete, the temp array will contain last 2 elements of original array.

Does JavaScript populate empty array items?

I am coding a lot of annual data in JavaScript, and I was considering adding it to arrays, using the year as the array index and putting the data into the array. However, Firebug seems to be indicating that JavaScript handles this by populating two thousand odd entries in the array with "undefined." With hundreds of such arrays kicking around in active memory, I'm worried the overhead of hundreds of thousands of useless array items could start to slow the program down. Will it?
When you set the value of a numeric index higher than the current length of your array, the length property is affected.
In brief, you should use an Object:
var data = {};
data[year] = "some data";
// or
var data = {
2009: "2009 data",
2010: "2010 data"
};
Now I answer the question title: "Does JavaScript populate empty array items?"
No, as I said before, only the length property is changed, (if necessary, only if the index added is larger than the current length), length is incremented to be one more than the numeric value of that index.
The Array.prototype methods work assuming that the array object will have its indexes starting from zero.
The previous indexes don't really exist in the Array object, you can test it:
var array = [];
array[10] = undefined;
array.hasOwnProperty(10); // true
array.hasOwnProperty(9); // false
In conclusion, arrays are meant to contain sequential indexes, starting from zero, if your properties don't meet those requirements, you should simply use an object.
Yes, most likely. You should consider using a JavaScript object instead:
var years = {2009: 'Good', 2010: 'Better'};
Well, if you iterate over many thousands of undefined, it will affect overall program speed, not sure if you'll notice it though.
On the other hand, sometimes a sparse array is simpler to use than a custom object,
and arrays have such handy methods available.
In a calendar application I begin with objects for each year in use, but each year consists of a twelve member (months array) and each 'month' is a sparse array of significant dates, whose lengths depend on the highest date of that month that has any data.

Categories