JavaScript 'In' Operator for 'Undefined' Elements in Arrays

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:

  1. 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.

  2. Which is easier to read?

    This is probably subjective as well. The second form is extremely common in JavaScript, FWIW.

  3. Which is less typing?

    The second form is obviously a lot shorter than the first.

  4. 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, since 0 is a number but if (0) won't go into the body of the if.

    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 value undefined. If you need to differentiate those at some point (again, not for the use case you mentioned), you'd use hasOwnProperty 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:

  1. Let to be ToObject(target).
  2. ReturnIfAbrupt(to).
  3. If only one argument was passed, return to.
  4. Let sources be the List of argument values starting with the second argument.
  5. For each element nextSource of sources, in ascending index order, do

    1. If nextSource is undefined or null, let keys be an empty List.
    2. 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

  1. Let spreadRef be the result of evaluating AssignmentExpression.
  2. Let spreadObj be GetValue(spreadRef).
  3. Let iterator be GetIterator(spreadObj).
  4. 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



Leave a reply



Submit