for-in でオブジェクトを走査する

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

for-in でオブジェクトを走査する とは

オブジェクトのキーを順番に取り出せる for-in 文を学び、値の合計を求める関数を書いてみよう。

for-in はオブジェクトのキーを順番に取り出すループ

前のレッスンでは配列向けの for-of を学びました。今度はオブジェクト向けのループ、for-in を学びます。{ name: 'Alice', age: 30 } のようなオブジェクトの キー (プロパティ名) を順番に取り出すための構文です。

JavaScript のオブジェクトはキーと値の集まりで、Python の辞書 (dict) や JavaMap に相当します。配列が「順番が大事なデータの並び」なら、オブジェクトは「名前で値を引くデータ」です。例えばユーザー情報 { name: 'Alice', age: 30, city: 'Tokyo' } のように、各値にラベル (キー) が付いています。

オブジェクトの中身を 1 つずつ見たいとき、for-of は使えません。なぜなら普通のオブジェクトは iterable (反復可能) ではない からです。代わりに for-in を使います。

JavaScript

const scores = { math: 80, english: 90, science: 70 }; for (const subject in scores) { // subject に 'math', 'english', 'science' が順に入る }

ループの中で subject (キー) から値を取り出すには、scores[subject] のように ブラケット記法 を使います。

JavaScript

const scores = { math: 80, english: 90, science: 70 }; let total = 0; for (const subject in scores) { total += scores[subject]; } // total は 240

for-in 文の文法

JavaScriptfor-in 文の基本形は次のとおりです。

JavaScript

for (const キー in オブジェクト) { // キーを使った処理 }

大事なポイントは次の通りです。

  • in キーワード を使う (of ではない、混同しないように)
  • 左辺の 変数にはキー (プロパティ名) が文字列で入る
  • 値そのものは入らない ので、obj[key] で取り出す必要がある
  • for-of と違って、これは オブジェクト専用 と思っておくとよい

JavaScript のオブジェクトのキーは原則として 文字列 (または Symbol) です。数値を書いても自動的に文字列に変換されます。なので for-in で取り出されるキーは常に文字列型だと覚えておきましょう。

for-in と for-of の違い

初心者が一番混乱するのが for-infor-of の違いです。次の表で整理してみます。

JavaScript

const arr = ['a', 'b', 'c']; // for-in は インデックス (文字列) を取る for (const i in arr) { // i は '0', '1', '2' } // for-of は 値そのものを取る for (const v of arr) { // v は 'a', 'b', 'c' }

配列にも for-in は使えてしまうのですが、インデックスが文字列で返ってくる うえに、配列のプロトタイプに追加されたプロパティまで拾ってしまう可能性があり、推奨されません。配列は for-of か通常の for、オブジェクトは for-in、これを鉄則にしましょう。

Object.keys や Object.entries との比較

モダンな JavaScript ではオブジェクトを走査するとき、for-in よりも Object.keysObject.entries を使う流派も増えています。

JavaScript

const scores = { math: 80, english: 90, science: 70 }; // Object.keys でキーの配列を取得 → for-of で回す for (const key of Object.keys(scores)) { // key にキーが入る } // Object.entries で キーと値のペアを取得 for (const [key, value] of Object.entries(scores)) { // key と value が両方手に入る }

メリットは、配列を経由するので Array.filterArray.map と組み合わせやすい ことと、プロトタイプチェーンを辿らないので 安全 だということです。for-in を使うか、Object.keys / Object.entries を使うかは好みですが、新規コードでは後者を選ぶ人が増えています。

実行フローを図で追ってみる

for (const key in obj) のループの流れは次の通りです。

diagram (will load when visible)

通常の for のような「初期化・条件・更新」ではなく、「次のキーを取って、あれば本体実行、なければ終了」というシンプルな流れです。

よくある間違い

for-in 文を使い始めるとき、初心者がハマる落とし穴が 3 つあります。

  • 値が直接入ると勘違いfor (const score in scores) { total += score; } のように書くと、score には キー (文字列) が入っているので、'math' + 0 = 'math' のような文字列連結になってしまいます。値が欲しいときは必ず scores[score] で取り出しましょう
  • 配列に for-in を使う — 動作はするのですが、インデックスが文字列で返ってくるうえ、プロトタイプの汚染にも弱いので非推奨です。配列は for-of か通常の for を使いましょう
  • ofin を取り違えるfor (const x of obj) と書くと普通のオブジェクトでは TypeError が出ます (iterable ではないため)。逆に for (const i in arr) だとインデックスが文字列で返るのが分かりにくくバグの温床です

for-in継承されたプロパティ も列挙してしまうという落とし穴があります。本格的なプロダクションコードでは Object.prototype.hasOwnProperty.call(obj, key) でフィルタする、もしくは初めから Object.keys を使うのが安全です。今回の課題ではオブジェクト直下のプロパティしか扱わないので気にしなくて OK ですが、頭の片隅に入れておきましょう。

もうひとつ、for-in のループの順序は 挿入順 が原則になっています (ES2015 以降)。{ a: 1, b: 2, c: 3 } を回すと a, b, c の順でキーが返ります。古いブラウザでは順序が保証されないこともありましたが、最近の JavaScript では安心して使えます。

やってみよう

それでは今回の課題に取り組みましょう。やることは次の通りです。

  1. sumValues(obj) 関数の中身を埋める
  2. for-in 文を使ってオブジェクト obj の全ての値を合計して返す
  3. sumValues({ a: 1, b: 2, c: 3 })6sumValues({ x: 10, y: 20 })30sumValues({})0 になるかを確認する

ヒントとして、合計用変数 total0 で初期化し、for (const key in obj) でキーを回し、total += obj[key]; で値を加算します。最後に return total; を忘れないようにしましょう。

余裕があれば、Object.values(obj).reduce((a, b) => a + b, 0) のような高階関数版でも書いてみてください。for-in だけでなく、Object.keys Object.values Object.entries の使い方も覚えると、オブジェクト処理の引き出しが広がります。

この先のレッスンでは break continue のループ制御や、ネストしたループ、素数列挙のような少し応用的なループへと進んでいきます。for-in はオブジェクトを扱う上で基礎中の基礎なので、ぜひ手を動かして覚えましょう。

よくある質問

Q. ブラウザと Node.js で動作は変わりますか?

A. JavaScript の構文・組み込み機能(配列・文字列・Promise 等)はどちらでも同じです。違いは DOM API(ブラウザ専用)と fs などのファイル系(Node 専用)など、実行環境固有の機能です。コアロジックは両方で動くため、テストもしやすくなります。

Q. ES6 以降の新機能は使っても大丈夫ですか?

A. 現代のブラウザ・Node.js は ES2020 以降の機能をほぼサポートしています。古い IE 等を対象にする必要が無ければ、let/const、アロー関数、分割代入、async/await を積極的に使ってください。トランスパイル(Babel/Vite)が必要なケースは年々減っています。

Q. TypeScript に進む価値はありますか?

A. 型情報があるとリファクタやエディタ補完が劇的に効くため、規模が大きくなるなら TypeScript への移行を強くおすすめします。最初は any を許容しつつ徐々に型を付ければ、JS の知識をそのまま活かせます。

次のレッスン

次は break と continue でループを制御する で、オブジェクトのキーを順番に取り出せる for-in 文を学び、値の合計を求める関数を書いてみよう を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. for-in の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. for-in とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. 関数名は sumValues、引数はオブジェクト obj 1 つ、戻り値は数値
  2. 繰り返し処理には必ず for-in 文を使うこと (Object.valuesreduce ではなく)
  3. 値はすべて数値であることを前提としてよい

入出力例

test-cases.txt

sumValues({"a":1,"b":2,"c":3})6 sumValues({"x":10,"y":20})30 sumValues({})0 sumValues({"english":90,"math":80,"science":70})240

ヒント

main.js
main.js
学習モード

メモ

for-in でオブジェクトを走査する

⌘S で保存