If I have an array
let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';
How do I iterate over just the properties (prop1
and prop2
) in this case?
If I have an array
let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';
How do I iterate over just the properties (prop1
and prop2
) in this case?
I'm not aware of any way to directly iterate just the plain properties. But, you can construct a list of just the plain properties as follows:
ar.keys()
gives you only the array element indexes (not plain properties)
Object.keys(ar)
gives you all properties and array indexes.
So, you can start with all the properties and indexes and then filter out the ones that are array element indexes and be left with just the properties.
let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';
let arrayIndexes = new Set(Array.from(ar.keys(), i => "" + i));
console.log("arrayIndexes (converted to string)", Array.from(arrayIndexes));
let props = Object.keys(ar).filter(k => !arrayIndexes.has(k));
console.log("props", props);
Or, the same concept with fewer temporary objects:
let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';
let arrayIndexes = new Set();
for (let i of ar.keys()) {
arrayIndexes.add("" + i);
}
console.log("arrayIndexes (converted to string)", Array.from(arrayIndexes));
let props = Object.keys(ar).filter(k => !arrayIndexes.has(k));
console.log("props", props);
If you want all own properties, including non-enumerable properties, you could do this:
let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';
let arrayIndexes = new Set();
for (let i of ar.keys()) {
arrayIndexes.add("" + i);
}
console.log("arrayIndexes (converted to string)", Array.from(arrayIndexes));
let props2 = Object.keys(Object.getOwnPropertyDescriptors(ar)).filter(k => !arrayIndexes.has(k));
console.log("props2", props2);
Examining Characters in Property Name
Note: I attempted a solution that iterates all the properties (including array indexes) and filters out things that will be interpreted as array indexes by examining the characters in the property name. This proved to be troublesome. It's not just as simple as whether it's a numeric value or not or converts to a number. To be considered an array index, here's what I've discovered so far:
Number.isSafeInteger()
because it can be a number, but too large to be an integer without some loss of precision. When converted to a number, it must also be less than ar.length
.x[1]
.Here are some numeric-like strings that are not interpreted as array indexes:
"01"
"00"
"9999999999999999999999999999999"
"-1"
"-0"
"1.0"
Here's is such a test, though I don't find it simpler than the above tests:
let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';
ar["-1"] = 'minus one';
ar["0"] = 'a';
ar["-0"] = 'z';
ar["01"] = 'zzzz';
ar["1.0"] = 'aaa';
ar["999"] = 'Z';
ar["99999999999999999999999999999999"] = 'ZZ';
ar["+0"] = 'bbb';
let props = Object.keys(ar).filter(k => {
// a property that isn't "0", but starts with "0" will not be an array index
// for example, "01" is not an array index
if (k.length > 1 && k.startsWith("0")) {
return true;
}
// if not entirely made up of digits 0-9, keep it
if (!/^\d+$/.test(k)) {
return true;
}
// convert to number
let index = +k;
// if it's not a safe integer or it's longer than the length,
// then it must not be an array index
if (!Number.isSafeInteger(index) || index >= ar.length) {
return true;
}
// if it passed all these tests, then it must be an array index so filter it out
return false;
});
console.log("props", props);
You can extract the keys from the array portion and identify the non-configurable properties.
let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';
const keys = [...ar.keys()].map(k => `${k}`);
console.log(
Object.getOwnPropertyNames(ar).filter(n => {
const desc = Object.getOwnPropertyDescriptor(ar, n);
return desc.enumerable && desc.configurable && !keys.includes(n);
}),
);