What this code means ([1, 2, 3] || 0)[0] - javascript

Closure(Script) implementation in JavaScript called "wisp" has this snippet:
(get [1 2 3] 1) ; => ([1, 2, 3] || 0)[0]
which means that the wisp code compiles to this in JavaScript:
([1, 2, 3] || 0)[0]
But why is || 0 part there?

My guess is that instead of writing a literal array, you can send it a variable:
(get x 1) ;
So the || 0 is used in case x is null or undefined.
In JavaScript, || does not return a boolean, it returns the 1st value if it's "truthy" and the 2nd if the 1st is "falsy".
0[0] returns undefined just like [][0] does. 0 is one less character than [], so that's probably why they did || 0 instead of || [].

Normally it would be used to specify a default value if the first part is null/undefined/false.
For example, consider the following code:
var a = 1;
var b;
var x = a || 0;//if a is true use a, otherwise use 0
var y = b || 0;//if b is true use b, otherwise use 0
The value of x will be 1, because 1 is a truthy value and therefore becomes the value that is used. whereas y will be 0 because the value of b is undefined (a falsey value) so the 0 is used instead.
In your example however it is pointless, as [1, 2, 3] will always be true. But if you was using a variable then is would be possible for the variable to not be assigned, so the default value would then apply.
Here is an example that shows how different types would apply
Here is more information on Truthy and Falsey vaues

I don't believe that the || 0 does anything. The || operator checks for a null, undefined etc, and if found will return the right hand side of the expression.
In this example [1, 2, 3] is never null so 0 will never be returned.

Related

Ternary operator condition

The following code uses the reduce method. It outputs the number of times an element appears in the array. If element appears once then it only outputs 1, otherwise if it is a repeated item then it it is added..
let a = ["a", "b", "c", "a", "b"]
const t = a.reduce((aa, ll) => {
const count = aa[ll];
count
?
aa[ll] = count + 1 :
aa[ll] = 1
return aa
}, {})
console.log(JSON.stringify(t))
// output
// { "a":2, "b":2, "c":1 }
Question is regarding the condition in the ternary operation, specifically the count variable. How is the count variable able to resolve true or false.
The concept is called "Truthy" and "Falsy" respectively. Ie anything that is different from false, 0, -0, 0n, NaN, null, undefined and "" (empty string) can be evaluated to true in Javascript
So your assign var counter = aa[ll] to be the value of the key ll in object aa. That's either a number or undefined. If it's a number !== 0 it's a truthy, if it's 0 or undefined it's falsy. Thus it can be used in a ternary operatator. Depending on the value of counter either the value of the first or the second assignment will be returned (but ignored). The return value of an assignment is always the value of the right hand side ...
While you can use also assignments in the expressions of a ternary operator, I personally wouldn't use it that way but write this assignment as follows
const t = a.reduce((aa, ll) => {
aa[ll] = (aa[ll] || 0) + 1;
return aa
}, {})
(aa[ll] || 0) will return the value of aa[ll] if it's a truthy or 0 otherwise. Thus, in your case, the result of this expression will always be a number >= 0. Then you increase the result by 1 (for the currenct occurence of ll) and assign it back to aa[ll]. This is much shorter than your original code and IMHO much more readable
Heres the answer I found.
"A javascript object consists of key-value pairs where keys are unique. If you try to add a duplicate key with a different value, then the older value for that key is overwritten by the new value."
basically the count variable was checking to see if the new property already exists.

Pure beginner javascript - array and map

Im brand new to javascript. I simply have an exercise where i need the array numbers to add each index to the next one.
The result should be:
var result = [4,8,15,21,11];
I tried something like this. It works on the first 4 indexes, however gives NaN on last since it doesnt have a next number to add to.
var numbers = [1, 3, 5, 10, 11];
var result = numbers.map((x, index) => x + numbers[index + 1])
console.log(result)
Is there any smarter way to do this and how do i get index 4 to stay as 11? And please explain as im trying to learn.
That's a perfectly valid way to do it. You can handle the last index by taking advantage of the fact that numbers[index] will be undefined (not an error) if index is beyond the end of the array, and undefined is a falsy value, so you can use || 0 to use 0 instead of undefined if it's there:
var result = numbers.map((x, index) => x + (numbers[index+1] || 0));
Live Example:
var numbers = [1, 3, 5, 10, 11];
var result = numbers.map((x, index) => x + (numbers[index + 1] || 0));
console.log(result);
|| is the logical OR operator, but it's a bit special in JavaScript: It evaluates its left-hand operand and, if that value is truthy¹, takes that value as its result; if the left-hand is falsy, the operator evalutes the right-hand operand and takes that value as its result.
In ES2020+ you can use the new nullish coalescing operator (??) instead of ||:
var result = numbers.map((x, index) => x + (numbers[index+1] ?? 0));
That will only use the 0 if numbers[index] is null or undefined, rather than if it's any falsy value.
¹ "truthy" and "falsy" - A truthy value is a value that evaluates to true when used as a condition (for instance, if (x) or x || y); a falsy value is a value that evalutes to false. The falsy values are 0, "", NaN, null, undefined, and of course, false. All other values are truthy. (Except there's a special case on browsers: document.all is falsy for legacy reasons.)

Why does Array.sort discriminate NaN vs undefined even after coercing the value in the comparator? [duplicate]

This question already has answers here:
javascript array.sort with undefined values
(3 answers)
Closed 4 years ago.
I don't understand why the output of the code below separates the NaN to the beginning of the array and the undefined values to the end of the array. I'm coercing the NaN and undefined to 0 so I would expect for them to be collocated (grouped) together.
[9, undefined, NaN, undefined].sort(
function (a, b) {
// coercing NaN or undefined to 0
const first = (Number.isNaN(a) || a === undefined) ? 0 : a;
const second = (Number.isNaN(b) || b === undefined) ? 0 : b;
if (first < second) {
return -1;
} else if (first > second) {
return 1;
} else {
return 0;
}
}
)
actual output is
[
NaN,
9,
undefined,
undefined
]
expected output would be
[
9,
NaN,
undefined,
undefined
]
// or some variant where the NaN and undefined are grouped together
[
NaN,
undefined,
undefined
9,
]
update
From the linked duplicate answer, I kind of understand that undefined values have a special rule applied to them (where they are pushed to the end of the array), but it still seems odd that this rule of handling undefined values get applied before the comparator can have a chance at processing the undefined value.
Does anyone know if it's a pre-filter that's going on when Array.sort is invoked? Akin to
[undefined, 9]
.filter(x => x !== undefined)
.sort(...)
In which case, would it be less processing to simply remove the undefined values a priori, if I needed to call Array.sort multiple times on an array containing undefined values? I'm trying to get a sense of how the internals of Array.sort is behaving.
You do the array.sort in which first they simply ignore undefined and second you convert NaN to 0, so obviously in sort 0 comes first and 9 is second
In array.sort, all non-undefined array elements are sorted according
to the return value of the compare function (all undefined elements
are sorted to the end of the array, with no call to function)
More information : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description
var test = [9, undefined, NaN, undefined].sort(
function (a, b) {
// coercing NaN or undefined to 0
const first = (Number.isNaN(a) || a === undefined) ? 0 : a;
const second = (Number.isNaN(b) || b === undefined) ? 0 : b;
console.log(first, second);
if (first < second) {
return -1;
} else if (first > second) {
return 1;
} else {
return 0;
}
}
);
console.log(test);

Operators === and ||

I wonder, are these absolutely the same:
var a = something1.something2 === undefined ? 1 : something1.something2;
var b = something1.something2 || 1;
No. In the first one, something1.something2 has to be undefined in order to get the value 1. In the second one it merely has to be falsy. There are lots of falsy values: 0, "", NaN, null, undefined, and of course, false.
No they are not.
If you take the value 0 for something1.something2, then in the first case the return value is 0.
The second case returns 1, because of the falsy value of 0.

How to test equality of an Array containing undefined elements?

I encountered some weird behavior in the assert module of node.js. I'm not sure if this is a bug or intended behavior.
When I create an array, and then initialize a value for example on the 3nd element, the first two elements are undefined. Testing whether the first two elements are undefined return true, however comparing the array as a whole with undefined for the first two elements fails.
var assert = require('assert');
var a = [];
a[2] = 2;
console.log(a); // [ , , 2 ]
assert.equal(a[0], undefined); // ok
assert.equal(a[1], undefined); // ok
assert.equal(a[2], 2); // ok
assert.deepEqual(a, [, , 2]); // ok
assert.deepEqual(a, [undefined, undefined, 2]); // error ???
I can understand there is a difference between undefined elements and elements having a value undefined, as Array extends Object, and array elements are just properties on an object.
But why does the last assertion fail?
Most likely because [,,2] and [undefined,undefined,2] don't have the same number of keys:
> Object.keys([,,2]).length
1
> Object.keys([undefined,undefined,2]).length
3
Here is the relevant part of the assert code that is checking this.
I'm going to step out on a limb here...
Most likely, deepEqual uses the hasOwnProperty method of object to make sure it doesn't catch any inherited properties.
If this is the case, the following console log may help you:
var test1 = [];
undefined
test1[2] = "xyzzy";
"xyzzy"
test1.hasOwnProperty(0)
false
test1.hasOwnProperty(1)
false
test1.hasOwnProperty(2)
true
var test2 = [undefined, undefined, "plugh"];
undefined
test2.hasOwnProperty(0)
true
test2.hasOwnProperty(1)
true
test2.hasOwnProperty(2)
true
I'm going to hazard a guess. It appears that there is a distinction between array locations that contain nothing (i.e., uninitialized) and are undefined, versus actually sticking an actual undefined into a specific location in an array.
You can actually see this in Chrome on the console log:
> a = [undefined, undefined, 2];
[undefined, undefined, 2]
> b = [,,2]
[undefined × 2, 2]
> c = [];
> c[2] = 2;
> c
[undefined × 2, 2]
It's a very subtle difference; in the first case ([,,2]) you created an array without anything in locations 0 and 1; it's a spare array where only location 3 contains a value. In the second case, you created an array which isn't a spare array. Instead you specifically inserted the value undefined at locations 1 and 2. It appears that both these cases are treated differently which is probably why they are not equal.
I think it might be easier to visualize the first array as [,,2] = [uninitialized, uninitialized, 2]. So the distinction is between an uninitialized value versus a concrete undefined that has been inserted into the array (which means the location is not uninitialized).
UPDATE
I took a look at the code in assert.js. Specifically, pay attention to lines 221 and 222:
if (ka.length != kb.length)
return false;
ka and kb are set in lines 213-215:
var ka = Object.keys(a),
kb = Object.keys(b),
key, i;
The value of Object.keys(a).length for [,,2] is 1 whereas the value of Object.keys(b).length for [undefined, undefined, 2] is 3. This is why they are not equal.
Not a bug. Those arrays have different behaviour. Consider the following use case:
function log(item, idx) {
console.log(item, " - ", idx);
}
a.map(log); // 2 - 2
[undefined, undefined, 2].map(log); //undefined - 0 undefined - 0 2 - 2

Categories