JavaScript 'in' operator for `undefined` elements in Arrays
Arrays are nothing but normal JavaScript objects, with some specialization. Quoting from Array Objects section of ECMA 5.1 Specification
Array objects give special treatment to a certain class of property names. A property name P (in the form of a String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 232−1.
So, array indices are nothing but properties of array objects. Now, lets see about missing elements in an Array.
Quoting from the ECMA 5.1 Standard Specification,
Array elements may be elided at the beginning, middle or end of the
element list. Whenever a comma in the element list is not preceded by
an AssignmentExpression (i.e., a comma at the beginning or after
another comma), the missing array element contributes to the length of
the Array and increases the index of subsequent elements. Elided array
elements are not defined. If an element is elided at the end of an
array, that element does not contribute to the length of the Array.
So, when you say
b = [1,,,,3];
except the elements 1 and 3, all others are treated as missing elements in the array. Now elements 1
and 3
correspond to the properties 0
and 4
(Array indices start with 0
in JavaScript).
You can check that like this
console.log(Object.getOwnPropertyNames(b));
# [ '0', '4', 'length' ]
console.log(Object.getOwnPropertyNames(a));
# [ '0', '1', '2', '3', '4', 'length' ]
Only the indices 0
and 4
are in b
. You might be wondering why a
has the properties from 0
to 4
, though the values are undefined
. Because, the elements at indices 1, 2 and 3 are defined to be undefined
, where as in b
, we don't know what those values are. That is why they are not allocated to a property (index).
Now, you are checking if 1
is in b
, with in
operator. Quoting from in
operator MDN Doc,
The in operator returns true if the specified property is in the specified object.
So, you are basically checking if 1
is one of the properties of b
, which is not. That is why '1'
in b
returns false
.
Note:
If you wanted to know if 1
is in the Array or not, you should use Array.prototype.indexOf
, like this
console.log(a.indexOf(1));
# 0
console.log(b.indexOf(1));
# 0
console.log(b.indexOf(5));
# -1
Array.prototype.indexOf
returns -1
if the element being searched is not there in the Array. So, you might want to do something like this
console.log(a.indexOf(1) !== -1);
# true
console.log(b.indexOf(1) !== -1);
# true
console.log(a.indexOf(5) !== -1);
# false
empty elements in arrays are not evaluated to undefined?
"Old style" array iterators (forEach
and friends) don't iterate over indexes that don't physically exist in an array ("holes"):
a = [0, 1, , 3, 4]
a.forEach(x => console.log(x))
elements in my array is undefined, but outside cycle they are good
In javascript the "," operator return the right value.
So when you write PO[z, r], it's like you write PO[r].
I guess you want to write PO[z][r] = P1[z][r] instead.
Best way to check undefined element in an array
"Best" is a fairly subjective term, so let's throw some objective criteria at it:
Which is faster?
In a way that you'll notice in the real world, neither. But if you really want to know, measure your specific code on the engines you want to support.
Which is easier to read?
This is probably subjective as well. The second form is extremely common in JavaScript, FWIW.
Which is less typing?
The second form is obviously a lot shorter than the first.
Which is more reliable?
Given your use case, they're equally reliable, because you're either not storing an entry in the array at all, or storing a non-
null
object reference.In similar but different use cases, you might need to be wary of the difference: The first form will only be true for an entry that exists and doesn't have the value
undefined
in it. The second form will be true for anything that isn't falsey. There are several falsey values:null
,undefined
,0
,""
,NaN
, and of course,false
. So you wouldn't use the second form to decide whether there was a number at a position in the array, since0
is a number butif (0)
won't go into the body of theif
.Speaking of ambiguity, note that comment about "...true for an entry that exists and doesn't have the value
undefined
in it..." Neither of the examples you gave differentiates between an entry that doesn't exist and an entry that exists with the valueundefined
. If you need to differentiate those at some point (again, not for the use case you mentioned), you'd usehasOwnProperty
on the array:if (array.hasOwnProperty(row))
The second solution is shorter, so it could be quicker to execute, because JavaScript is an interpreted scripting language
This is a mistaken assumption. Most modern JavaScript engines are just-in-time optimizing compilers. V8 (the engine in Chrome), for instance, is a two-stage optimizing compiler: On the first pass, it turns your JavaScript into machine code quickly, and then if it identifies hotspots (bits of code that run a lot), it goes back and does more aggressive optimization there.
Fill undefined elements in an array
You can use Array.from()
to iterate over all array values, while also providing a mapping function. The mapping function can either take the first value from otherValues
or it will use the current element if it isn't undefined. This will modify ohterValues
, however, you can do a shallow clone of it before running Array.from using .slice()
if need be.
See example below:
const myArray = new Array(9)
const otherValues = [2,3,1,6,7,9];
myArray.splice(1, 1, 2);
myArray.splice(5, 1, 4);
myArray.splice(7, 1, 5);
const res = Array.from(myArray, x => x === undefined ? otherValues.shift() : x);
console.log(res);
Spreading undefined in array vs object
As noted in the comments, and summarized by @ftor from #687, object spread is equivalent1 to Object.assign() (issues #687, #45), whereas spread in array literal context is iterable spread.
Quoting Ecma-262 6.0, Object.assign() is defined as:
19.1.2.1 Object.assign ( target, ...sources )
The assign function is used to copy the values of all of the enumerable own properties from one or more source objects to a target object. When the assign function is called, the following steps are taken:
- Let to be ToObject(target).
- ReturnIfAbrupt(to).
- If only one argument was passed, return to.
- Let sources be the List of argument values starting with the second argument.
- For each element nextSource of sources, in ascending index order, do
- If nextSource is undefined or null, let keys be an empty List.
- Else, ...
...followed by the description of copying own properties. The draft of Object Rest/Spread Properties is here. It is not a part of the Ecma-262 6.0.
A SpreadElement in an array literal expression is defined to begin as follows:
SpreadElement : ... AssignmentExpression
- Let spreadRef be the result of evaluating AssignmentExpression.
- Let spreadObj be GetValue(spreadRef).
- Let iterator be GetIterator(spreadObj).
- ReturnIfAbrupt(iterator).
And since undefined
does not have a property with the key @@iterator, a TypeError is thrown, based on the steps of GetIterator. The standard is not an easy read, but if I'm not mistaken, the path to error is GetIterator -> GetMethod -> GetV -> ToObject, which throws a TypeError for undefined and null.
A simple remedy to using variables with possibly undefined value in array initialization is to use a default:
const maybeArray = undefined;
const newArray = [ ...(maybeArray || []) ];
1: There is a difference in how setters are handled.
Javascript array value is undefined ... how do I test for that
array[index] == 'undefined'
compares the value of the array index to the string "undefined".
You're probably looking for typeof array[index] == 'undefined'
, which compares the type.
TypeScript: How to use Array.includes with possibly undefined value (optional chaining) on array of strings
One way is to cast the array to an array of string | undefined
. This is probably my preferred way as it doesn't leave any traces in the transpiled JavaScript code. Also it still prevents you from accidentally checking against e.g. a number
which actually doesn't make sense.
const someTest = (['creditCard', 'payPal'] as (string | undefined)[]).includes(someObj.paymentData?.paymentMethod);
It is a bit verbose in the TypeScript code tho, so there are two more options:
Checking upfront if someObj.paymentData?.paymentMethod
is defined:
const someTest = !!someObj.paymentData?.paymentMethod && ['creditCard', 'payPal'].includes(someObj.paymentData.paymentMethod);
Ensuring the value is always a string by falling back to an empty string in case of undefined
using the nullish coalescing operator (??
):
const someTest = ['creditCard', 'payPal'].includes(someObj.paymentData?.paymentMethod ?? '');
For further reference, here is a related thread on GitHub abut the typing of the Array.includes
function: https://github.com/microsoft/TypeScript/issues/26255
How to optimise a condition which checks for each object of an array of objects whether specific properties are not undefined?
I understand the Q. in a way that the OP does not want to check for every object's value being not the undefined
value, and the OP rather wants to check whether some predefined properties (e.g. by a key list) need to be present and also not equal to undefined
.
In this case one could implement a function which checks via a list of property names (key list) for each passed item/object whether such an object fulfills the above mentioned requirement.
Such a function could be passed as callback function to every
in order to detect whether any of an array's item/object does fulfill the requirement.
function isEveryPropertyFromBoundListDefined(item) {
const keyList = this; // the bound list of defined keys.
return keyList
.every(key => item[key] !== undefined);
}
const sampleData_1 = [{
a: 12, b: 14, c: undefined, d: undefined,
e: 56, f: "file 1", g: "file 2", h: undefined,
}];
const sampleData_2 = [{
e: 56, f: "file 1", g: "file 2", h: null,
}, {
h: 1, g: 2, f: 3, e: 4,
}];
const listOfToBeDefinedKeys = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
console.log(
sampleData_1
// make use of `every`'s 2nd `thisArg` parameter.
.every(isEveryPropertyFromBoundListDefined, listOfToBeDefinedKeys)
);
console.log(
sampleData_2
// make use of `every`'s 2nd `thisArg` parameter.
.every(isEveryPropertyFromBoundListDefined, ['e', 'f', 'g', 'h'])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Related Topics
The Preferred Way of Creating a New Element with Jquery
Orderby Multiple Fields in Angular
Reactjs: Prevent Multiple Times Button Press
Differencebetween Unary Plus/Number(X) and Parsefloat(X)
How to Remove a Class from Elements in Pure JavaScript
Capture the Close Event of Popup Window in JavaScript
How to Have Jquery Restrict File Types on Upload
How Can Print Preview Be Called from JavaScript
Can Read-Only Properties Be Implemented in Pure JavaScript
Ajax Call and Clean JSON But Syntax Error: Missing ; Before Statement
Firing a Keyboard Event on Chrome
External Template in Underscore
Javascript/Jquery: Set Values (Selection) in a Multiple Select
Trying to Use the Domparser with Node Js
Chrome Extension: Get Page Variables in Content Script
React - Getting a Component from a Dom Element for Debugging