for in statement in JS

A loop for key-value data structures 

Note The for in statement is a dated loop syntax. Since its creation, better alternatives have surfaced, including the newer for of loop syntax and built-in data type methods to support loop like functionality.

The for in loop syntax was the first construct designed to loop over JavaScript key-value data structures. Unlike the for, while and do while statements which don't necessarily peg loops to data structures, that is, they can run loops with 'n' iterations using counters and conditionals irrespective of a data structure, the for in statement requires loops to be made against a data structure.

The syntax of the for in statement consists of a key-value data structure declared after the in keyword to loop over each element in the data structure. In between the for and in keywords is a variable to hold the key of each element on every iteration. After the in keyword, a block statement in curly brackets {} is declared to execute business logic on each iteration which has access to the variable declared in between the for and in keywords. Like other JavaScript loops, the block statement of a for in statement can also make use of break and continue statements -- including label statements -- to exit a loop or iteration prematurely.

The for in loop is designed to iterate over key-value data structures using the following rules:

To fully grasp what these last two rules mean for a for in loop, it's inevitable to take a closer look at JavaScript data types that have key-value data structures.

The most versatile key-value data structure in JavaScript is the Object data type and it's precisely this data structure that the for in loop is more suited to, as well as the data structure where you can potentially face these two rules associated with for in loops. Now, this doesn't mean the Object data type is the only key-value data structure in JavaScript, other data types like the Array data type & the String data type also operate as key-value data structures, so they too can be run through a for in loop.

However, because data types like Array and String always have elements with simple key integer values and lack the concept of element values being enumerable -- not to mention these data types have other built-in loop mechanisms -- the for in statement is almost always used on Object data types.

When it comes to the Object data type and the for in rules mentioned in the prior list, you really need to go out of you way to face them. Most keys added to Object data types aren't Symbol's and most values added to Object data types are enumerable by default. To make the key of an Object data type a Symbol you need to explicitly create a Symbol data type and to make the value of an Object data type non-enumerable you must explicitly set its enumerable property descriptor to false, which can only be done with the Object.defineProperty() or Object.defineProperties() methods.

Listing 6-5 illustrates the use of the for in statement on Object data types and for the sake of completeness also shows how the for in statement can be used on String and Array data types.

Listing 6-5. For loops with for in statements
let language = "JavaScript";
let vowels = ["a","e","i","o","u"];
let languages = {JavaScript: {
                  creator:"Eleanor Eich"
                  }
                }

// Loop over String
for (const key in language) { 
  console.log(`langauge 
  key: ${key};
  value: ${language[key]}`);
}

// Loop over Array
for (const key in vowels) { 
  console.log(`vowels
  key: ${key};
  value: ${vowels[key]}`);
}

// Loop over Object
for (const lang in languages) {
  console.log(`languages
  key: ${lang};
  value: ${JSON.stringify(languages[lang])}`);
}

// Loop over nested Object property
for (const version in languages.JavaScript) { 
  console.log(`languages.JavaScript
  key: ${version};
  value:${languages.JavaScript[version]}`);
}

// Add properties to object in various forms
// Dot notation
languages.TypeScript = {creator:"Microsoft Corporation"};
// Object.defineProperty as not enumerable
Object.defineProperty(languages, "Ruby", {
  value: {creator:"Yukihiro Matsumoto"},
  enumerable: false
});
// Object.defineProperty as enumerable 
Object.defineProperty(languages, "Rust", {
  value: {creator:"Graydon Hoare"},
  enumerable: true
});
// Bracket notation
languages["WebAssembly"] = {creator:"World Wide Web Consortium (W3C)"};
// Bracket notation, with Symbol key
languages[Symbol.for("Python")] = {creator:"Guido van Rossum"};

// Inspect languages object 
// All properties are there and output
console.log("languages: %o", languages);


// Loop over languages object
// Non-enumerable values ("Ruby") and Symbol keys ("TypeScript") are ignored by for in
for (const lang in languages) { 
  console.log(`languages
  key: ${lang}; 
  value: ${JSON.stringify(languages[lang])}`);
}

Listing 6-5 starts by declaring three data types: a String, an Array and an Object. Next, you can see for in loop are applied to each one. In each case, notice the variable declared in between the for and in keywords is used inside the loop's block statement, to output a log message with the key and also use the key to access its associated value.

The for in loop output for String language and Array vowels confirms the prior statement about keys in these kind of data structures, all keys are integers. In addition, notice the associated values for each key constitute either a String character or an Array element. The Object languages then has two for in loop applied to it, the first one loops over the entire language object, while the second loop uses the JavaScript key to loop over the nested object languages.JavaScript.

The more interesting aspect in listing 6-5 is the section where various elements are added to Object languages. First, a dot notation is used to add the TypeScript key with a value of {creator:"Microsoft Corporation"};. Next, two more elements are added to languages with the Object.defineProperty() method: the "Ruby" key with a property descriptor enumerable: false and a value of {creator:"Yukihiro Matsumoto"}; and the "Rust" key with a property descriptor enumerable: true and a value of {creator:"Graydon Hoare"}. Finally, two additional elements are added to languages with a bracket notation: the "WebAssembly" key with a value of {creator:"World Wide Web Consortium (W3C)"}; and the Symbol key Symbol.for("Python") with a value of {creator:"Guido van Rossum"}.

Once the new elements are added to Object languages, a plain console object log statement is used to output the contents in languages to confirm all new elements are present. However, the last statement in listing 6-5 performs a for in loop over the updated languages object where you can see the elements with the "Ruby" key and Symbol.for("Python") key aren't output. The reason these elements aren't output, is because the "Ruby" key value is marked with a property descriptor of enumerable: false and the key Symbol.for("Python") is a Symbol, both of which are skipped in for in loops.