【JavaScript】ループと配列とは?基本的な使い方を初心者向けにわかりやすく解説
はじめに
JavaScriptで何か処理を行うとき、ループと配列の組み合わせは非常によく使われます。
たとえば、ユーザーが入力したデータを順番にチェックしたり、Webアプリケーションで取得したアイテム一覧を表示するときなど、配列に格納された複数の要素を順番に処理する場面が多いからです。
しかし、いざコードを書こうとすると「そもそも配列って何?」「いくつも種類があるループ構文は何が違うの?」と疑問に思うかもしれませんね。
ここでは、JavaScriptにおけるループと配列の基本的な概念を押さえつつ、実務でもよく見るような利用シーンを踏まえて、やさしく解説します。
JavaScriptをまだ深く触ったことがない方でも理解しやすいように丁寧に進めますので、ぜひ一緒に見ていきましょう。
きっと「この処理はどのループがいいんだろう?」や「ここではどうやって配列を操作すればいいんだろう?」といった疑問が解消されるのではないでしょうか。
この記事を読むとわかること
- 配列の基本的な構造と役割
- JavaScriptのループ文の種類と、配列と組み合わせる方法
- よく使われる配列操作(追加・削除・検索・加工など)の方法
- 配列とループを使うことで解決できる実務での活用シーン
- 配列の要素を効率的に扱うためのより高度なループ構文の使い方
配列の基礎を押さえよう
JavaScriptの配列は、複数の要素をひとまとめに管理するデータ構造です。
たとえば、文字列や数値、オブジェクトなどをまとめて格納できるので、同時に扱いたい要素があるときにとても便利です。
JavaScriptでは、配列は**[]
**(角括弧)を使って宣言します。
以下は簡単な例です。
const fruits = ["apple", "banana", "orange"];
このように定義されたfruits
という配列には、3つの文字列が格納されています。
後で解説するように、ループを使えばこのfruits
に格納されている要素を順番に処理できます。
配列の要素数やインデックス
配列の大きな特徴は、要素が 順番 (インデックス)を持っていることです。
先ほどのfruits
であれば、以下のようなイメージになります。
- インデックス0:
"apple"
- インデックス1:
"banana"
- インデックス2:
"orange"
このように、インデックスを使って個々の要素を取り出すことができます。
以下の例では、fruits[0]
が"apple"
という文字列にアクセスする様子を示しています。
console.log(fruits[0]); // "apple"
実務での利用イメージ
実務においては、サーバーや外部APIから受け取ったデータが配列形式で返ってくるケースが多々あります。
たとえば、ECサイトでは商品一覧が配列として返ってきて、フロントエンド側でその配列をループしながらページに商品カードを表示するといったシーンが考えられます。
配列とループはセットで使うことが多いので、次のセクションではループ文の詳細を見ながら、配列を繰り返し処理する方法をじっくり解説します。
ループとは何か
ループは**「同じ処理を複数回繰り返す」**ための仕組みです。
JavaScriptにはさまざまなループ構文が存在しますが、基本的な考え方は「要素をひとつずつ取り出して処理する」というところにあります。
ここでは、代表的なループ構文として**for
、while
、do...while
、for...in
、for...of
**などを紹介していきます。
for文の基本
もっとも代表的なループといえば**for
**文です。
以下のように書きます。
for (let i = 0; i < 5; i++) { console.log(i); }
let i = 0
は初期化i < 5
は条件式i++
は更新式
ここでは、i
が0から4まで変化しながら、ループ内のconsole.log(i)
が5回実行されます。
配列と組み合わせる場合
配列と組み合わせて使うfor
文の例を見てみましょう。
const fruits = ["apple", "banana", "orange"]; for (let i = 0; i < fruits.length; i++) { console.log(fruits[i]); }
このコードでは、i
が0からfruits.length - 1
までインクリメントしながら進みます。
つまり、配列の全要素を順番に取り出してconsole.log
しているわけですね。
実務では、商品の一覧を取得した後に在庫数をチェックしたり、ユーザー一覧から特定のユーザー情報を拾い出すときなどに、このfor
文がよく使われます。
while文とdo...while文
while
文は、指定した条件がtrueの間、処理を繰り返す仕組みです。
以下の例では、count
が3未満の間はループを続けるように設定しています。
let count = 0; while (count < 3) { console.log("Count is: " + count); count++; }
一方、**do...while
**文は、必ず最初に一度処理を実行した後で条件をチェックします。
以下の例だと、count
が条件を満たさない場合でも、少なくとも1回はコンソールに出力が行われます。
let count = 0; do { console.log("Count is: " + count); count++; } while (count < 3);
実務上では、繰り返し処理の前に何らかの初期値を必ず処理してから、続けるべきか判断するときにdo...while
文を使う場面があります。
ただし、for
文の方が直感的に書ける場合が多いため、頻度としてはfor
文が中心になることが多いといえるでしょう。
for...in文とfor...of文
JavaScriptには、オブジェクトや配列を扱うために**for...in
文とfor...of
**文があります。
for...in
は、主にオブジェクトのキーをループ処理したいときによく使われます。
配列に対して使うこともできますが、配列のインデックスを取得するという動きになります。
const user = { name: "Alice", age: 25 }; for (const key in user) { console.log(key, user[key]); } // name Alice // age 25
一方の**for...of
は、配列の要素そのもの**を手軽に取り出したいときに便利です。
以下の例のように、インデックス不要で値を取得できるため、コードがシンプルになります。
const fruits = ["apple", "banana", "orange"]; for (const fruit of fruits) { console.log(fruit); } // apple // banana // orange
実務的にも、配列の要素をそのまま使いたい場合はfor...of
を使うと見通しがよくなることが多いです。
逆に、インデックスが必要な場合は従来のfor
文を選ぶほうが分かりやすいでしょう。
配列の操作方法
配列は、要素を増やしたり削除したり、あるいは部分的に取り出したりといった操作を頻繁に行います。
ここでは、代表的な操作を挙げながら、それぞれの利用シーンを見ていきます。
要素の追加や削除
配列の末尾に要素を追加するなら**push()
、先頭に追加するならunshift()
が定番です。
一方、削除は末尾ならpop()
、先頭ならshift()
**がそれぞれ対応します。
const fruits = ["apple", "banana"]; fruits.push("orange"); // 末尾に追加 -> ["apple", "banana", "orange"] fruits.unshift("melon"); // 先頭に追加 -> ["melon", "apple", "banana", "orange"] fruits.pop(); // 末尾を削除 -> ["melon", "apple", "banana"] fruits.shift(); // 先頭を削除 -> ["apple", "banana"]
実務では、ユーザーの入力に応じて配列に新しいデータを加えたり、不要になったデータを取り除く場面がイメージしやすいでしょう。
たとえば、リアルタイムチャットでメッセージ履歴を配列で管理しているなら、新しいメッセージをpush()
で追加していくケースが考えられます。
指定位置の編集:splice()
配列の中間地点に要素を差し込んだり削除したりする場合には、**splice()
**がよく使われます。
以下の例では、インデックス1の位置から1つの要素を削除し、新しく「kiwi」を挿入しています。
const fruits = ["apple", "banana", "orange"]; fruits.splice(1, 1, "kiwi"); // -> ["apple", "kiwi", "orange"]
- 最初の引数(1)は開始位置
- 2番目の引数(1)は削除する要素の個数
- その後に続く引数("kiwi")は挿入したい要素
このように、実務の画面UIにおいて、リスト表示の真ん中にユーザーがアイテムを追加する要望があった場合などに、このsplice()
が役立ちます。
部分的な要素の抽出:slice()
**slice()
**は元の配列を部分的に抜き出して、新たな配列を作るメソッドです。
slice()
の引数には開始位置と終了位置を指定します(終了位置の要素は含まれません)。
const fruits = ["apple", "banana", "orange", "melon"]; const someFruits = fruits.slice(1, 3); // -> ["banana", "orange"]
元の配列は変更されず、新たに["banana", "orange"]
という配列が作られます。
一方で、先ほどのsplice()
は元の配列を破壊的に変更するため、使い分けに注意が必要です。
実務では、データの安全性を考慮して、元の配列を変えたくないときはslice()
、直接編集したいときはsplice()
と分けるのが一般的です。
map, filter, reduce
JavaScriptには、配列の要素を一括で処理するための高階関数がいくつか提供されています。
代表的なのは**map()
、filter()
、reduce()
**です。
map()
配列の各要素に特定の処理を施して、新しい配列を作るためのメソッドです。
以下では、数値の配列に対して2倍した結果を新しい配列として得ています。
const numbers = [1, 2, 3]; const doubled = numbers.map((num) => num * 2); // -> [2, 4, 6]
実務シーンでは、サーバーから受け取ったデータを加工して画面表示用に整形したりするときに便利です。
たとえば、金額に対して税計算を行うなどの処理を全要素に一括で適用できるでしょう。
filter()
条件に合う要素だけを抜き出し、新たな配列を生成するメソッドです。
以下の例では、数値の配列から奇数だけを抜き出しています。
const numbers = [1, 2, 3, 4, 5]; const oddNumbers = numbers.filter((num) => num % 2 !== 0); // -> [1, 3, 5]
実務では、商品の在庫があるものだけを抽出したり、ユーザーが活性状態のものだけを取り出すといった場面が想定されます。
配列の中から必要な要素だけをピックアップする用途に最適です。
reduce()
配列のすべての要素をひとつの値にまとめるためのメソッドです。
たとえば、数値の合計を求めるだけでなく、オブジェクトを加工しながら集計処理するといった複雑なことも可能です。
const numbers = [1, 2, 3, 4]; const sum = numbers.reduce((acc, current) => { return acc + current; }, 0); // -> 10
acc
はこれまでの処理結果の累積値current
は現在の要素- 第2引数の
0
は初期値
実務で大きなデータを扱うときにも、reduce()
を使った集計処理がよく登場します。
たとえば、売上データの合計を出したり、配列をオブジェクトに変換するロジックなどに利用できるはずです。
配列操作において、同じような操作を繰り返し行う際は高階関数の活用を検討すると読みやすいコードになりやすいです。
ループと配列を組み合わせた実務イメージ
では、具体的にループと配列をどのように組み合わせて実務で使うのか、いくつかイメージを挙げてみましょう。
ここでは、Webアプリケーションのフロントエンドを念頭に置いた事例を示します。
ユーザー一覧の表示
ユーザー情報が配列で返ってくるケースを考えます。
たとえば、ユーザー名とメールアドレスが格納された配列users
を受け取ったら、for
文やfor...of
文で繰り返し処理をして、HTMLとして画面に組み込むといったことが一般的です。
const users = [ { name: "Alice", email: "alice@example.com" }, { name: "Bob", email: "bob@example.com" }, ]; for (const user of users) { console.log(user.name, user.email); }
これをUI上に表示させるときは、ループの中でDOM操作をするか、あるいはテンプレートエンジンなどを使って要素を生成します。
リアルタイムでユーザーリストを更新するときなどは、配列を更新して再ループすれば、最新の状態を画面に描画できるでしょう。
商品リストのフィルタリング
ECサイトなどでは、配列で渡された商品データの中から、特定のカテゴリーや価格帯に合致するものだけを表示することがよくあります。
そんなときは、**filter()
**やfor
文を活用し、条件に合った要素だけを残すのが定番です。
const products = [ { name: "Laptop", price: 1200 }, { name: "Mouse", price: 20 }, { name: "Monitor", price: 200 }, ]; // 価格が100以下の商品だけを表示 const cheapProducts = products.filter((product) => product.price <= 100); console.log(cheapProducts);
こうすることで、ユーザーが「100以下の商品だけ見たい」というリクエストをした場合でも、素早くリストを絞り込むことができます。
ユーザーインターフェースで「並び替え」や「ソート」を行うときも同様にループや配列操作がベースにあります。
分割したデータ処理とページネーション
大量のデータを一度にユーザーに見せると、画面が非常に長くなって操作しにくくなります。
そこでページネーション機能を導入し、配列の中身を一定の要素数ごとに区切って表示する方法があります。
function paginate(array, itemsPerPage, currentPage) { // 配列をページ単位に分割する const startIndex = (currentPage - 1) * itemsPerPage; return array.slice(startIndex, startIndex + itemsPerPage); } const products = /* たとえば50件の商品データが入っている想定 */; const pageData = paginate(products, 10, 1); // 1ページ目は10件だけ取得 console.log(pageData);
例えば10件ずつ表示するなら、ループやslice()
を使いながらページ番号に応じて取得範囲を変えれば、ユーザーは一覧をスムーズに閲覧できます。
ページ番号を操作するUIを用意すれば、その都度異なる配列範囲をループ処理して画面にレンダリングできるでしょう。
エラーデータの取り除きや警告
配列に混在しているエラーデータや不正な値があった場合は、filter()
やsplice()
などで除去したり、ループを回してログを取るケースもあります。
業務システムであれば「数値であるべき項目に文字列が入っているかどうか」をチェックして、誤ったデータだけ別の配列に振り分けるといったこともよくあるでしょう。
たとえば、数字のみが許される配列で文字列が混ざっていないかチェックするイメージは以下のように書けます。
const mixedData = [100, "apple", 200, "banana", 300]; const validNumbers = []; const errors = []; for (const item of mixedData) { if (typeof item === "number") { validNumbers.push(item); } else { errors.push(item); } } console.log(validNumbers); // [100, 200, 300] console.log(errors); // ["apple", "banana"]
このように、ループで配列の要素をひとつひとつ確認しながら振り分けることが多いです。
実務ではこうしたバリデーション処理やログ取得のために活用されます。
無限ループが起こらないように、条件式には注意しましょう。テストデータで動作確認せずに本番環境で実行すると、想定外のところで処理が止まらなくなる場合があります。
より高度なテクニック:配列と反復処理の組み合わせ
ここまでは標準的なループと配列の操作について解説しましたが、実際にはもっと複雑なケースもあります。
複数の配列を一度に扱いたいときや、大量のデータを部分的にしか処理したくないケースなどでは、もう少し高度なやり方を検討することがあります。
ネストされたループ
配列の中にオブジェクトが入っていて、そのオブジェクトのプロパティにさらに配列がある場合は、 ネスト (入れ子) になったループが必要です。 たとえば、ユーザーとそのユーザーがもつ複数の投稿データをまとめて処理したいときなどです。
const users = [ { name: "Alice", posts: ["Hello World!", "JavaScript is fun"] }, { name: "Bob", posts: ["I love coding", "Data structures are interesting"] } ]; for (const user of users) { console.log(user.name + "さんの投稿:"); for (const post of user.posts) { console.log(post); } }
この例では、ユーザーごとの投稿を2重のループで順に処理しています。
ネストが深くなりすぎるとコードが読みにくくなるため、適切に関数化や処理の分割を行うことが大切です。
reduce()を使った集計・加工の例
先述のreduce()
はさまざまな集計や加工を一度に行えるため、複雑なロジックに便利です。
たとえば、以下の例では、ユーザー配列をreduce()
でまとめて加工し、name
だけのリストを作成するイメージです。
const users = [ { name: "Alice", age: 25 }, { name: "Bob", age: 30 }, { name: "Charlie", age: 22 } ]; const names = users.reduce((acc, user) => { acc.push(user.name); return acc; }, []); console.log(names); // ["Alice", "Bob", "Charlie"]
ここでは単純に名前の配列を作っていますが、もっと複雑なデータ構造への加工も実現可能です。
集計や変換ロジックがひとつの関数で書けるため、場合によっては複雑なネストを回避しやすくなります。
ループを使わない方法:forEach()やメソッドチェーン
forEach()
メソッドを使うことで、ループ文を書かずとも配列に対して繰り返し処理を実行できます。
以下の例では、単にコンソールに出力するだけですが、コールバック内で必要な処理を行えます。
const fruits = ["apple", "banana", "orange"]; fruits.forEach((fruit, index) => { console.log(index, fruit); });
加えて、JavaScriptの配列操作はメソッドチェーンと呼ばれる書き方が可能です。
たとえば、filter()
で絞り込みを行い、その結果にmap()
を続けて実行するといった流れを、連続して書けるわけです。
const products = [ { name: "Laptop", price: 1200 }, { name: "Mouse", price: 20 }, { name: "Monitor", price: 200 }, { name: "USB Cable", price: 5 } ]; const affordableProducts = products .filter(p => p.price < 100) .map(p => p.name); console.log(affordableProducts); // -> ["Mouse", "USB Cable"]
このように、ループ構文なしでも繰り返し処理を行いながら配列を変換し、スッキリしたコードを書けるのが魅力です。
実務では、複数の条件を組み合わせてデータを加工する際に、このやり方を多用するケースがあります。
配列とループで気をつけたいポイント
ここからは、配列とループを組み合わせる際によくある注意点についてまとめます。
動作が予想と違って混乱しやすい部分なので、しっかり確認しておきましょう。
配列の要素数が変化する場合
ループを回しながら配列の末尾を削除したり挿入したりしていると、要素数が変化し続けてしまい、意図せずループが早く終わってしまう・終わらないといったトラブルが発生することがあります。
以下のように、ループ中に配列の長さが変わる操作を行うと、i
のインデックスが想定しない動きをするケースがあり、バグを招きがちです。
const fruits = ["apple", "banana", "orange"]; for (let i = 0; i < fruits.length; i++) { if (fruits[i] === "banana") { fruits.splice(i, 1); // bananaを削除 } console.log(fruits[i]); }
こうした操作が必要な場合は、ループ前に削除対象をまとめて取り除くか、一時的な配列に結果を蓄えるなどの工夫をするのが一般的です。
高階関数内のreturnとbreakの違い
for
やwhile
などのループ文では、break
を使うとループを強制終了できますが、map()
やfilter()
などの高階関数内でreturn
を行っても、そのメソッド自体が終了するわけではありません。
高階関数は「コールバック関数を各要素に対して実行」し続ける仕組みなので、return
は「その1要素における戻り値」を返しているだけです。
ループを途中で打ち切りたい場合には、従来のfor
文やfor...of
文を使う方法、あるいはすべての要素を回し終わるしかない場合があることを覚えておきましょう。
スコープと変数の取り扱い
ループ内でlet
を使った変数宣言を行う場合、スコープの概念を理解しておく必要があります。
for
文やwhile
文の外で宣言した変数に、ループの中で値を代入するときは、ループ終了後の値が予期せぬ形で残ってしまうこともあります。
実務では、ループブロック内で使う変数は極力ループのブロック内に閉じ込めておくと、ほかの処理と混ざる可能性を下げられます。
まとめ
JavaScriptでデータを扱う際、配列とループは切っても切れない関係にあります。
配列に格納された複数の要素を、いかに効率的かつミスなく処理するかは、実務でも頻繁に求められる知識です。
- 配列は複数の要素をまとめるデータ構造
- ループには
for
やwhile
、for...of
などさまざまな構文がある - 要素数の変動やスコープなど注意点を押さえる
map()
やfilter()
といった高階関数を使うと、コードを短く書きやすい- 配列をネストした構造や、条件付きのループなど高度な状況でも応用は可能
「ループと配列の使い方は一通りイメージできた」という方は、さらにさまざまな配列メソッドやオブジェクト操作などにも興味を広げていくと良いかもしれません。
開発の現場でデータを扱う際に、この基本知識が大いに役立つはずです。