for in
statement in JS
A loop for key-value data structures
Note Thefor in
statement is a dated loop syntax. Since its creation, better alternatives have surfaced, including the newerfor 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:
- All key-value elements are processed by a
for in
loop, except those elements that have a key as aSymbol
data type. - All key-value elements are processed by a
for in
loop, so long as an element's value is enumerable.
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.