What delete really means in NodeJS in arrays? - javascript

Reading the answers to this question I understand that delete sets undefined value for the element.
So, if we have an array: a = [1, 2, 3] (at least) on the client side using delete a[1] the array would become [1, undefined x 1, 3] that is true, on the client side.
What happens in NodeJS?
But testing same operation on NodeJS (server side) I get the following:
$ node
> a = [1, 2, 3]
[ 1, 2, 3 ]
> delete a[1]
true
> a
[ 1, , 3 ]
> a[1] = undefined
undefined
> a
[ 1, undefined, 3 ]
>
So, a[1] = undefined sets undefined value. But what does the space mean between the two commas ([1, , 3])? delete put it! Why?
I guess that the space means undefined, but why it doesn't appear as undefined only I set it as undefined?
Why is this happening?

When you use delete on an array entry, it removes that entry from the array. The array length is not changed, but that slot in the array is gone. If you try to look at the value of that slot it will show as undefined though deleting it is not quite the same as setting it to undefined. You can read about deleting an array entry here (scroll down to the subtitle "Deleting Array Elements").
This reference also explains the difference between using delete on an array entry vs. setting it to undefined. The difference is subtle and only makes a difference in a few situations. Here's an example from that article:
// using delete
var trees = ["redwood","bay","cedar","oak","maple"];
delete trees[3];
if (3 in trees) {
// this does not get executed
}
// setting to undefined
var trees = ["redwood","bay","cedar","oak","maple"];
trees[3]=undefined;
if (3 in trees) {
// this gets executed
}
So, this console output:
[ 1, , 3 ]
is showing you that the second array entry is missing (it's gone completely).
See this article http://perfectionkills.com/understanding-delete/ for a full run-down of how the delete operator works in other circumstances.

Array indexes at language level work like any other properties.
When you access a property that's not there, the proto chain is walked until a result is found or undefined is returned if nothing is found. But you can also use undefined as a normal value.
There can be major differences, eg:
Array.prototype[1] = 15;
var a = [1, 2, 3];
delete a[1];
//protip: the above can be written as `var a = [1,,3]`
var b = [1, undefined, 3];
//Logs 15 and undefined
console.log(a[1], b[1]);
In a, because the property 1 is not there, the prototype is checked and the value 15 is found.
In b, because the property 1 is there, the value of the property (undefined) is just directly returned.
Also the console formatted output for an array is usually different between environments but that doesn't really change what is actually going on.

Related

Why is Array.map returning different results? [duplicate]

This question already has answers here:
JavaScript "new Array(n)" and "Array.prototype.map" weirdness
(14 answers)
Closed last year.
According to my current JS understanding, All 3 lines below should return the same result, can someone please explain what I'm missing ?
1- Unexpected
[...Array(6).map((x) => 1)]
(6) [undefined, undefined, undefined, undefined, undefined, undefined]
2- expected
[...Array(6)].map((x) => 1)
(6) [1, 1, 1, 1, 1, 1]
3- unexpected
Array(6).map((x) => 1)
(6) [empty × 6]
Array(n) creates an array of length n, but the array has no values in it so there is nothing to map over.
Basically, this:
const a = Array(n);
is the same as:
const a = [];
a.length = n;
This is why the created array has no elements in it. Attempting to map it will do nothing since there are no values to map.
You can, however, convert that empty array to an array of undefined values. This can be done in two ways:
One is using the spread operator as you have done:
[...Array(n)] // creates new array [undefined, undefined, undefined ...] n times
The other, more commonly used, is .fill(), which sets its parameter as the value for all the indices of the array. To fill with undefined, do:
Array(n).fill() // fills array with [undefined, undefined, undefined ...] n times
An array of undefined, unlike an empty array, has elements and can therefore be mapped:
[undefined, undefined, undefined].map(x => 1) // [1, 1, 1]
So you can first convert your empty array to an array of undefined, and then map it like so:
Array(n).fill().map(x => 1)
If you want to fill the array with the same element in each position like in this example, you can simply pass it in to fill without needing to map the array:
Array(n).fill(1)

JS Splice Array of Nulls Removes Last Index Regardless of Parameters

For a reason specific to this application an array of data or nulls is used to display a list of forms. The difference is based on whether data was provided by a service or manually added, where null indicates everything was manually added via
a button not a service.
So ignoring the use case of an array of nulls it turns out that [null, null, null].splice(0, 1); removes the null at index 2 instead of 0 based on entering values into the different forms displayed based on the array length, and then seeing who disappears on delete.
It can be made to work by adding something unique like { index: theIndex } to the array instead of null. So splice now works correctly removing the item at index 0 instead of 2, but now I'm curious what splice does under the covers that it can't remove an index regardless of its value compared to the other indices.
Can anyone explain what splice is doing? Based on the spec for splice I don't really see why this happens.
(This follows from the comments in the question but is too large to be a comment).
So I think you are having a conceptual misconception (^^). Look at this examples:
let a = [1, 2, 3]
a.splice(0, 1) // => 1
a // => [2, 3]
let b = [1, 2, 3]
delete b[0] // => true
b // => [<1 empty slot>, 2, 3]
The splice function modifies the array in-place. Note that, although we spliced the first element, we got as a result an array of two elements.
Look now at this example
let a = [1, 1, 1]
a.splice(0, 1)
a // => [1, 1]
let b = [1, 1, 1]
b.splice(2, 1)
b // => [1, 1]
We are deleting the first element from a and the last from b, but of course there's no way of telling so just looking at the result.
In the case with the nulls, the same thing is happening. Some library (Angular) is trying to figure out which element you deleted, but there's no way of knowing. This is because null === null.
Now if you use an array of empty objects, for example, there would be a way of knowing. Since {} !== {}---because each time you cast a {} you are creating a unique object---, then you could know which element is missing in an array of empty objects. The case is similar with the array [1, 2, 3].
let a = [{}, {}, {}] // lets imagine that each object has a unique id
// and we have [{}#1, {}#2, {}#3]
let [obj1, obj2, obj3] = a
obj1 === obj2 // => false, because they have different ids.
a.splice(0, 1)
a // => [{}#2, {}#3]
a.includes(obj1) // => false, because {}#1 is no longer in a
So an alternative to using an array of nulls, would be to use an array of empty objects. I think that is why the code works for you when you use objects like { index: theIndex }. But of course all depends on how smart Angular is. I bet there is a more native way of deleting an element, as #KaiserKatze points out, "it's always a bad idea to directly remove or add elements in the array if it maps to your model."
You have to understand that when you are splicing the array you're only doing that--removing an element from an array. You're not removing the "form element" when splicing the array. Instead, some foreign code is reading the array and trying to figure out --under the hood-- what you intended to do.

Why is destructuring and concatenation leaving different types of undefined in array? [duplicate]

I'm doing some small experiments based on this blog entry.
I am doing this research in Google Chrome's debugger and here comes the hard part.
I get the fact that I can't delete local variables (since they are not object attributes). I get that I can 'read out' all of the parameters passed to a function from the array called 'arguments'. I even get it that I can't delete and array's element, only achieve to have array[0] have a value of undefined.
Can somebody explain to me what undefined x 1 means on the embedded image?
And when I overwrite the function foo to return the arguments[0], then I get the usual and 'normal' undefined.
This is only an experiment, but seems interresting, does anybody know what undefined x 1 refers to?
That seems to be Chrome's new way of displaying uninitialized indexes in arrays (and array-like objects):
> Array(100)
[undefined × 100]
Which is certainly better than printing [undefined, undefined, undefined,...] or however it was before.
Although, if there is only one undefined value, they could drop the x 1.
Newer versions of Chrome use empty instead of undefined to make the difference a bit clearer.
For example, entering [,,undefined,,] in the DevTools console results in:
▼(4) [empty × 2, undefined, empty]
2: undefined
length: 4
▶__proto__: Array(0)
Google Chrome seems to choose to display sparse array using this undefined x n notation. It will show [undefined, undefined] if it is not a sparse array:
var arr = new Array(2);
console.log(arr);
var arr2 = [];
arr2[3] = 123;
console.log(arr2);
var arr3 = [,,,];
console.log(arr3)
var arr4 = [,];
console.log(arr4)
var arr5 = [undefined, undefined]
console.log(arr5)
var arr6 = [undefined]
console.log(arr6)
arr1 to arr4 are all sparse arrays, while arr5 and arr6 are not. Chrome will show them as:
[undefined × 2]
[undefined × 3, 123]
[undefined × 3]
[undefined × 1]
[undefined, undefined]
[undefined]
note the [undefined x 1] for the sparse array.
Since you deleted an element, it follows that: If you delete an element from an array, the array becomes sparse.
Here is how it looks, Type this code into chrome devtools console:-
arr = new Array(4);
arr[2] = "adf";
console.log(arr); // [undefined × 2, "adf", undefined × 1]
See the first two consecutive "undefined" that are represented by *2, to show that there are two consecutive undefined there
It just happened to me too. Open the same thing in another browser and output/log the array to the console. As long as it works in both crome and in firefox the same way (as in accessing the elements works fine even if the output has the undefinedx1), I consider it some kind of bug in Chrome not refreshing something internally. You can actually test it this way:
I was adding elements to an array, and firing console.log() to log the element, to check if it was undefined or not. They were all defined.
After push() I logged the array and it seemed that when this happened, it was always the last one being undefined x 1 on Chrome.
Then I tried logging it more times and also later, but with the same result.
However when I accessed (for the sake of output), the element by its index, it returned the proper value (funny).
After that I logged the array again and it said the last index was undefined x 1 (the very one I just accessed directly with success!).
Then I did a for loop on the array, logging or using each element, all showed up fine.
Then another log on the array, still buggy.
The code I'm originally using has checks for undefined and they didn't fire, but I was seeing all these undefineds in the console and it was bugging me (no pun intended).
Also worth reading: Unitialized variables in an array when using only Array.push? I'm using pop() too in my code and it's a reasonable explanation that the console log actually represents a different state in time (when the code is 'halted').

Javascript - what should setting a value at index -1 in an array do?

I've been surprised to find that:
var a = [1, 2, 3];
a[-1] = 4;
can have different results:
in chrome -> a is then [1, 2, 3]
in node -> a is then [1, 2, 3, '-1': 4]
what is the expected behavior, if any?
The result should be the same no matter what. Either one may display differently in the console, but in practice, the fashion in which values are displayed in the console shouldn't really have any bearings on the underlying mechanics. In this case, the effect is the same: the property named -1 is set to 4. However, you won't be able to iterate through this normally, unless you specifically set your start value to -1, and the value at -1 very probably (don't quote me on this) won't be reflected in the array's length property.
If you're interested, the ECMAScript specification for Arrays is here:
http://ecma-international.org/ecma-262/7.0/index.html#sec-array-objects
and the length property of Array instances is defined here:
http://ecma-international.org/ecma-262/7.0/index.html#sec-properties-of-array-instances-length

About pop() and push() in Javascript

I'm really a beginner in Javascript, and trying what i read as much as I can.
But when comes to pop() and push(), I get some strange results that I'm wondering about.
Here's the code :
var arr = [];
arr.push(2,3);
console.log(arr);
console.log(arr.pop());
console.log(arr);
the result is :
[2, undefined × 1]
3
[2]
Shouldn't it be :
[2, 3]
3
[2]
This is due to console.log's asynchronous evaluation on your browser. By the time the result of the first console.log has been displayed, the item is already gone because of pop().
For accurate results, call toString():
var arr = [];
arr.push(2,3);
console.log(arr.toString()); // 2,3 - as expected.
console.log(arr.pop());
console.log(arr);
You'd have to note that the console handles objects as "live". Any object (arrays, objects etc.) you already outputted on the console is still subject to operations.
That's why when you expected [2,3] on the first log, the code already popped the 3, thus replaced undefined on 3's spot.
Of course, this event depends on how the browser implement's their console.

Categories

Resources