2

I got a FileHelper-method that lists the name of all files and eventually returns the filenames:

import fs from 'fs';
import path from 'path';

class FileHelper {

    static ListFiles() {
        const fileDir = `${path.resolve()}/Specifications/`;
        let files = fs.readdirSync(fileDir);

        for (const file in files) {
            console.log(file); // Prints filename correctly: 'petstore.json'
        }

        return files;
    }

}

export default FileHelper;

However, when I invoke this method and print it once again in a for-loop it prints the array index and not the value:

import FileHelper from './Helpers/FileHelper.js';

 function main() {  
  try {
    let specifications = FileHelper.ListFiles();

    for (const specification in specifications) {   
      console.log(specification); // prints '0' instead of 'petstore.json'
    }
  }
  catch(err) {
    console.error(err);
  }
}


main();

enter image description here

Why does it not print the file name in the second for-loop? Thanks!

robtot
  • 863
  • 1
  • 9
  • 31
  • 1
    `for..in` iterates over keys only. So the second piece of code is correct. I'm not sure why the first one would be giving you names, `fs.readdirSync()` returns an array either way, so going over the keys of the array gives you indexes. – VLAZ Feb 20 '21 at 07:43
  • 2
    Does this answer your question? [Why is using "for...in" for array iteration a bad idea?](https://stackoverflow.com/questions/500504/why-is-using-for-in-for-array-iteration-a-bad-idea) – Arun Kumar Mohan Feb 20 '21 at 07:46
  • @ArunKumarMohan while that question is relevant, the question here is why does using `for..in` twice on the same array produce completely different results. – VLAZ Feb 20 '21 at 07:48
  • @VLAZ Yeah, you're right. But I'm unable to reproduce the issue. – Arun Kumar Mohan Feb 20 '21 at 07:53

2 Answers2

3

The reason you're seeing the key is because the for...in loop iterates over the keys of an array.

 function main() {  
  try {
    let specifications = FileHelper.ListFiles();

    for (const key in specifications) {
      
      const { [key]: value }= specifications;
      // ^ the above line is equivalent to
      //   const value = specifications[key]
      
      console.log(key, value);
    }
  }

  catch(err) {
    console.error(err);
  }
}

Check out the MDN documentation on for...in, which explains:

The for...in statement iterates over all enumerable properties of an object that are keyed by strings (ignoring ones keyed by Symbols), including inherited enumerable properties.

In the comments to your question, Arun linked to an excellent StackOverflow question that explains different ways of accomplishing your goal: why is using for...in for array iteration a bad idea

One of those alternative ways is for...of, which looks:

for (const specification of specifications) {
  console.log(specification);
}

The for...of statement creates a loop iterating over iterable objects, including: built-in String, Array, array-like objects (e.g., arguments or NodeList), TypedArray, Map, Set, and user-defined iterables. It invokes a custom iteration hook with statements to be executed for the value of each distinct property of the object.

Also, you can ierate over the array using Array.prototype.forEach(). Here's that approach:

specifications
  .forEach((currentKey) => {
    const { [currentKey]: specificationValue } = specifications;
    console.log(currentKey, specificationValue);
  })

Or you can do it the old fashioned way -- with a for loop:

for (let i = 0; i < specifications.length; i++) {
  console.log(i, specifications[i])
}
sbolel
  • 3,486
  • 28
  • 45
1

Avoid using for...in to iterate an array. Instead use for...of

Example

for (const specification of specifications) {
  console.log(specification);
}
brk
  • 48,835
  • 10
  • 56
  • 78