Today I faced with a problem and I fixed it but I didn't know why it occurs! So I decided to ask it here.
Let me show my problem fast and short:
function newProp(array) {
let returnArray = [];
for (i in array) {
returnArray.push(i);
}
return returnArray;
}
let underTest = [{
username: 'stackoverflow',
email: 'stackoverflow@noStack.com',
total: 2
}]
let input = [1, 2, 3, 4];
for (i in underTest) {
console.log('before call:', underTest[i]);
underTest[i].permutation = newProp(input);
console.log('after call the underTest[i] does not exist:', underTest[i]);
console.log('But the whole array still exists', underTest);
}
Code Explanation: I'm trying to add a new property called permutation
to each object of underTest
array.
Problem: I don't expect the underTest[i]
be undefined
after calling newProp()
function.
My Question: Why underTest[i]
is undefined
after calling newProp()
? I think it's because of using for in
over array in my code but in MDN web doc there is no hint about my problem, it just says:
Note: for...in should not be used to iterate over an Array where the index order is important.
Array indexes are just enumerable properties with integer names and are otherwise identical to general object properties. There is no guarantee that for...in will return the indexes in any particular order. The for...in loop statement will return all enumerable properties, including those with non–integer names and those that are inherited.
Because the order of iteration is implementation-dependent, iterating over an array may not visit elements in a consistent order. Therefore, it is better to use a for loop with a numeric index (or Array.prototype.forEach() or the for...of loop) when iterating over arrays where the order of access is important.
and due to above paragraphs while I don't mind the order of elements I should be able to use for in
structure(I don't mind the inherited properties too).
Solutions: By trial and error, I found so many solutions to solve the problem and I think it's useful to mention some of them:
- Replace
for in
with a traditionalfor
loop will solve the problem:
function newProp(array) {
let returnArray = [];
for (i in array) {
returnArray.push(i);
}
return returnArray;
}
let underTest = [{
username: 'stackoverflow',
email: 'stackoverflow@noStack.com',
total: 2
}]
let input = [1, 2, 3, 4];
for (let i = 0; i < underTest.length; i++) {
console.log('before call:', underTest[i]);
underTest[i].permutation = newProp(input);
console.log('after call the underTest[i] exists', underTest[i]);
}
- In
newProp
functions, If Instead of making the array dynamically, return an static array then the problem disappears(I didn't know why, yet!):
function newProp(array) {
return [1, 2, 3, 4];
}
let underTest = [{
username: 'stackoverflow',
email: 'stackoverflow@noStack.com',
total: 2
}]
let input = [1, 2, 3, 4];
for (i in underTest) {
console.log('before call:', underTest[i]);
underTest[i].permutation = newProp(input);
console.log('after call the underTest[i] exists', underTest[i]);
}
- you can use built-in
forEach
method ofunderTest
array and it will solve the problem too but I didn't like it because it can leads to callback hell problem(especially if you work with asynchronous jobs). So I didn't provide its snippet.
NOTE: If you can, please explain why the second solution will solve the problem too?