はじめに
前回の記事ではCSSのFlexboxを基礎から実践パターンまで解説しました。今回はJavaScriptで配列の中にある要素を表示する方法を解説します。「APIから取得したデータをページに表示したい」「商品リストを一覧表示したい」「条件に合うデータだけを絞り込んで表示したい」という場面はWeb制作で毎回のように登場します。配列の扱い方をしっかり理解することで、動的なWebページが作れるようになります。
1. 配列とは何か
1.1 配列の基本
/*
配列(Array):
複数のデータをひとまとめにして管理できるデータ構造。
[ ] で囲んで , で区切る。
インデックス(添字):
配列の何番目かを示す番号。
0から始まる。
*/
// 文字列の配列
const colors = [ 'red', 'blue', 'green', 'yellow' ];
// 数値の配列
const scores = [ 85, 92, 78, 95, 88 ];
// オブジェクトの配列(最もよく使う)
const users = [
{ id: 1, name: '山田太郎', age: 30 },
{ id: 2, name: '鈴木花子', age: 25 },
{ id: 3, name: '田中一郎', age: 35 },
];
// 要素へのアクセス(インデックスは0始まり)
console.log( colors[0] ); // 'red'
console.log( colors[1] ); // 'blue'
console.log( users[0].name ); // '山田太郎'
// 配列の長さ
console.log( colors.length ); // 4
2. 配列の要素を1件ずつ処理する
2.1 for ループ(古典的な方法)
const fruits = [ 'りんご', 'バナナ', 'オレンジ' ];
// for ループで1件ずつ処理する
for ( let i = 0; i < fruits.length; i++ ) {
console.log( i, fruits[i] );
// 0 りんご
// 1 バナナ
// 2 オレンジ
}
// for...of ループ(よりシンプルな書き方)
for ( const fruit of fruits ) {
console.log( fruit );
// りんご
// バナナ
// オレンジ
}
2.2 forEach(コールバック関数を使う方法)
const fruits = [ 'りんご', 'バナナ', 'オレンジ' ];
// forEach:配列の各要素に対してコールバック関数を実行する
fruits.forEach( ( fruit ) => {
console.log( fruit );
} );
// インデックスも取得できる
fruits.forEach( ( fruit, index ) => {
console.log( `${ index + 1 }. ${ fruit }` );
// 1. りんご
// 2. バナナ
// 3. オレンジ
} );
// オブジェクトの配列
const users = [
{ id: 1, name: '山田太郎', age: 30 },
{ id: 2, name: '鈴木花子', age: 25 },
{ id: 3, name: '田中一郎', age: 35 },
];
users.forEach( ( user ) => {
console.log( `${ user.name }(${ user.age }歳)` );
// 山田太郎(30歳)
// 鈴木花子(25歳)
// 田中一郎(35歳)
} );
/*
forEach の特徴:
→ 戻り値がない(undefined を返す)
→ break で途中終了できない
→ 単純にすべての要素を処理するときに使う
*/
3. map|配列を変換して新しい配列を作る
3.1 map の基本
/*
map:
配列の各要素を変換して
新しい配列を作るメソッド。
元の配列は変更しない。
変換後の値を return する必要がある。
*/
const numbers = [ 1, 2, 3, 4, 5 ];
// 各要素を2倍にする
const doubled = numbers.map( ( num ) => num * 2 );
console.log( doubled ); // [2, 4, 6, 8, 10]
console.log( numbers ); // [1, 2, 3, 4, 5](元の配列は変わらない)
// 各要素を文字列に変換する
const numStrings = numbers.map( ( num ) => `第${ num }章` );
console.log( numStrings );
// ['第1章', '第2章', '第3章', '第4章', '第5章']
// オブジェクトの配列から特定のプロパティだけ取り出す
const users = [
{ id: 1, name: '山田太郎', email: 'yamada@example.com' },
{ id: 2, name: '鈴木花子', email: 'suzuki@example.com' },
];
const names = users.map( ( user ) => user.name );
console.log( names ); // ['山田太郎', '鈴木花子']
// オブジェクトを別の形に変換する
const userCards = users.map( ( user ) => ( {
id: user.id,
label: `${ user.name }(ID: ${ user.id })`,
} ) );
console.log( userCards );
// [
// { id: 1, label: '山田太郎(ID: 1)' },
// { id: 2, label: '鈴木花子(ID: 2)' },
// ]
3.2 map でHTMLを生成する(最もよく使うパターン)
const products = [
{ id: 1, name: 'ノートPC', price: 89800, stock: 5 },
{ id: 2, name: 'マウス', price: 3800, stock: 12 },
{ id: 3, name: 'キーボード', price: 12800, stock: 0 },
];
// map でHTMLの文字列を生成して join で1つにつなげる
const productsHTML = products
.map( ( product ) => `
<article class="product-card" data-id="${ product.id }">
<h2 class="product-name">${ product.name }</h2>
<p class="product-price">
¥${ product.price.toLocaleString() }
</p>
<p class="product-stock ${ product.stock === 0 ? 'is-soldout' : '' }">
${ product.stock === 0
? '在庫なし'
: `残り${ product.stock }点`
}
</p>
<button
class="btn-add-cart"
${ product.stock === 0 ? 'disabled' : '' }
>
${ product.stock === 0 ? '売り切れ' : 'カートに追加' }
</button>
</article>
` )
.join( '' ); // 配列を文字列に連結する(区切り文字なし)
// DOMに挿入する
document.querySelector( '.product-list' ).innerHTML = productsHTML;
💡 map().join('') の組み合わせは実務で最もよく使うパターンです: map で各要素をHTML文字列に変換して・join('') で1つの文字列につなげて・innerHTML でDOMに挿入するという流れを覚えましょう。
4. filter|条件に合う要素だけを取り出す
4.1 filter の基本
/*
filter:
条件に合う要素だけを抽出して
新しい配列を作るメソッド。
コールバック関数が true を返した要素だけが残る。
元の配列は変更しない。
*/
const numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
// 偶数だけを取り出す
const evens = numbers.filter( ( num ) => num % 2 === 0 );
console.log( evens ); // [2, 4, 6, 8, 10]
// 5より大きい数だけを取り出す
const bigNumbers = numbers.filter( ( num ) => num > 5 );
console.log( bigNumbers ); // [6, 7, 8, 9, 10]
// オブジェクトの配列でfilterを使う
const products = [
{ id: 1, name: 'ノートPC', price: 89800, category: 'PC', stock: 5 },
{ id: 2, name: 'マウス', price: 3800, category: 'PC', stock: 12 },
{ id: 3, name: 'デスク', price: 45000, category: '家具', stock: 3 },
{ id: 4, name: 'キーボード', price: 12800, category: 'PC', stock: 0 },
{ id: 5, name: 'チェア', price: 68000, category: '家具', stock: 7 },
];
// カテゴリーが'PC'のものだけを取り出す
const pcProducts = products.filter(
( product ) => product.category === 'PC'
);
console.log( pcProducts );
// [ ノートPC・マウス・キーボードの3件 ]
// 在庫がある商品だけを取り出す
const inStock = products.filter( ( product ) => product.stock > 0 );
console.log( inStock );
// [ ノートPC・マウス・デスク・チェアの4件(キーボードを除く) ]
// 複数の条件を組み合わせる
const affordablePC = products.filter(
( product ) => product.category === 'PC' && product.price < 50000
);
console.log( affordablePC );
// [ マウス・キーボードの2件 ]
4.2 filter でUIの絞り込み機能を作る
// 商品の絞り込みフィルター
const products = [
{ id: 1, name: 'ノートPC', price: 89800, category: 'PC' },
{ id: 2, name: 'マウス', price: 3800, category: 'PC' },
{ id: 3, name: 'デスク', price: 45000, category: '家具' },
{ id: 4, name: 'キーボード', price: 12800, category: 'PC' },
{ id: 5, name: 'チェア', price: 68000, category: '家具' },
];
const productList = document.querySelector( '.product-list' );
const categoryBtns = document.querySelectorAll( '.filter-btn' );
// 商品を描画する関数
function renderProducts( items ) {
if ( items.length === 0 ) {
productList.innerHTML = '<p class="empty">該当する商品がありません</p>';
return;
}
productList.innerHTML = items
.map( ( product ) => `
<article class="card">
<h2>${ product.name }</h2>
<p>¥${ product.price.toLocaleString() }</p>
<span class="tag">${ product.category }</span>
</article>
` )
.join( '' );
}
// 初期表示:すべての商品を表示する
renderProducts( products );
// カテゴリーボタンのクリックで絞り込む
categoryBtns.forEach( ( btn ) => {
btn.addEventListener( 'click', () => {
// アクティブなボタンを切り替える
categoryBtns.forEach( ( b ) => b.classList.remove( 'is-active' ) );
btn.classList.add( 'is-active' );
const category = btn.dataset.category;
// 「すべて」ボタンの場合は全件表示
if ( category === 'all' ) {
renderProducts( products );
return;
}
// 選択されたカテゴリーで絞り込む
const filtered = products.filter(
( product ) => product.category === category
);
renderProducts( filtered );
} );
} );
/*
HTML側:
<div class="filter-buttons">
<button class="filter-btn is-active" data-category="all">すべて</button>
<button class="filter-btn" data-category="PC">PC</button>
<button class="filter-btn" data-category="家具">家具</button>
</div>
<div class="product-list"></div>
*/
5. map と filter を組み合わせる
5.1 チェーンして使う
const products = [
{ id: 1, name: 'ノートPC', price: 89800, stock: 5, category: 'PC' },
{ id: 2, name: 'マウス', price: 3800, stock: 12, category: 'PC' },
{ id: 3, name: 'デスク', price: 45000, stock: 0, category: '家具' },
{ id: 4, name: 'キーボード', price: 12800, stock: 0, category: 'PC' },
{ id: 5, name: 'チェア', price: 68000, stock: 7, category: '家具' },
];
// filter で在庫ありの商品を絞り込んでから
// map でHTMLを生成する
const html = products
.filter( ( product ) => product.stock > 0 ) // ① 在庫ありだけに絞る
.map( ( product ) => ` /* ② HTMLに変換する */
<article class="card">
<h2>${ product.name }</h2>
<p>¥${ product.price.toLocaleString() }</p>
</article>
` )
.join( '' ); // ③ 文字列に連結する
document.querySelector( '.list' ).innerHTML = html;
5.2 検索機能の実装
const items = [
{ id: 1, title: 'JavaScriptの非同期処理入門', tag: 'JavaScript' },
{ id: 2, title: 'CSSのFlexboxを完全理解する', tag: 'CSS' },
{ id: 3, title: 'ReactのuseStateとuseEffect', tag: 'React' },
{ id: 4, title: 'TypeScriptで型安全なコード', tag: 'TypeScript' },
{ id: 5, title: 'JavaScriptの配列操作まとめ', tag: 'JavaScript' },
];
const searchInput = document.querySelector( '#search' );
const resultList = document.querySelector( '#results' );
function renderItems( data ) {
if ( data.length === 0 ) {
resultList.innerHTML = '<p>検索結果がありません</p>';
return;
}
resultList.innerHTML = data
.map( ( item ) => `
<li class="result-item">
<span class="tag">${ item.tag }</span>
<a href="/posts/${ item.id }">${ item.title }</a>
</li>
` )
.join( '' );
}
// 初期表示
renderItems( items );
// 検索処理
searchInput.addEventListener( 'input', () => {
const query = searchInput.value.trim().toLowerCase();
if ( query === '' ) {
renderItems( items ); // 検索ワードが空なら全件表示
return;
}
// タイトルに検索ワードが含まれるものを絞り込む
const filtered = items.filter( ( item ) =>
item.title.toLowerCase().includes( query )
);
renderItems( filtered );
} );
6. その他の便利な配列メソッド
6.1 find・findIndex
/*
find:
条件に合う最初の要素を返す。
見つからなければ undefined を返す。
findIndex:
条件に合う最初の要素のインデックスを返す。
見つからなければ -1 を返す。
*/
const users = [
{ id: 1, name: '山田太郎', role: 'admin' },
{ id: 2, name: '鈴木花子', role: 'user' },
{ id: 3, name: '田中一郎', role: 'user' },
];
// IDが2のユーザーを取得する
const user = users.find( ( u ) => u.id === 2 );
console.log( user ); // { id: 2, name: '鈴木花子', role: 'user' }
// 管理者のインデックスを取得する
const adminIndex = users.findIndex( ( u ) => u.role === 'admin' );
console.log( adminIndex ); // 0
// 見つからない場合
const notFound = users.find( ( u ) => u.id === 999 );
console.log( notFound ); // undefined
6.2 some・every
/*
some:
条件に合う要素が1つでもあれば true を返す。
every:
すべての要素が条件を満たせば true を返す。
*/
const scores = [ 85, 92, 78, 95, 88 ];
// 90点以上が1つでもあるか
const hasHighScore = scores.some( ( score ) => score >= 90 );
console.log( hasHighScore ); // true
// すべての点数が70点以上か
const allPassed = scores.every( ( score ) => score >= 70 );
console.log( allPassed ); // true
// 在庫がある商品が1つでもあるか
const products = [
{ name: 'A', stock: 0 },
{ name: 'B', stock: 5 },
{ name: 'C', stock: 0 },
];
const hasStock = products.some( ( p ) => p.stock > 0 );
console.log( hasStock ); // true
// すべての商品が在庫切れか
const allSoldOut = products.every( ( p ) => p.stock === 0 );
console.log( allSoldOut ); // false
6.3 reduce
/*
reduce:
配列をひとつの値に集約するメソッド。
合計・最大値・グループ化などに使う。
*/
const numbers = [ 1, 2, 3, 4, 5 ];
// 合計を求める
const sum = numbers.reduce( ( total, num ) => total + num, 0 );
console.log( sum ); // 15
// 商品の合計金額を求める
const cart = [
{ name: 'りんご', price: 120, quantity: 3 },
{ name: 'バナナ', price: 80, quantity: 5 },
{ name: 'みかん', price: 150, quantity: 2 },
];
const totalPrice = cart.reduce(
( total, item ) => total + item.price * item.quantity,
0
);
console.log( totalPrice ); // 1060
// カテゴリーごとにグループ化する
const products = [
{ name: 'りんご', category: '果物' },
{ name: 'バナナ', category: '果物' },
{ name: 'にんじん', category: '野菜' },
{ name: 'みかん', category: '果物' },
{ name: 'キャベツ', category: '野菜' },
];
const grouped = products.reduce( ( groups, product ) => {
const key = product.category;
if ( !groups[key] ) groups[key] = [];
groups[key].push( product );
return groups;
}, {} );
console.log( grouped );
// {
// '果物': [ りんご・バナナ・みかん ],
// '野菜': [ にんじん・キャベツ ]
// }
6.4 sort
/*
sort:
配列を並び替えるメソッド。
⚠️ 元の配列を直接変更する(破壊的メソッド)
コピーしてから使う習慣をつける。
*/
const products = [
{ name: 'ノートPC', price: 89800 },
{ name: 'マウス', price: 3800 },
{ name: 'デスク', price: 45000 },
{ name: 'キーボード', price: 12800 },
];
// 価格の安い順に並び替える(元の配列を変えないようにコピー)
const sortedByPrice = [ ...products ].sort(
( a, b ) => a.price - b.price
);
console.log( sortedByPrice );
// マウス → キーボード → デスク → ノートPC の順
// 価格の高い順
const sortedByPriceDesc = [ ...products ].sort(
( a, b ) => b.price - a.price
);
// 名前の五十音順(文字列の場合)
const sortedByName = [ ...products ].sort(
( a, b ) => a.name.localeCompare( b.name, 'ja' )
);
7. 実践:投稿一覧をAPIから取得して表示する
class PostList {
#posts = [];
#filtered = [];
#container;
#searchInput;
#categoryBtns;
constructor( containerSelector ) {
this.#container = document.querySelector( containerSelector );
this.#searchInput = document.querySelector( '#search' );
this.#categoryBtns = document.querySelectorAll( '.category-btn' );
this.#init();
}
async #init() {
this.#showLoading();
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_limit=12'
);
this.#posts = await response.json();
this.#filtered = [ ...this.#posts ];
this.#render();
this.#bindEvents();
} catch ( error ) {
this.#showError();
}
}
#bindEvents() {
// 検索
this.#searchInput?.addEventListener( 'input', () => {
this.#applyFilters();
} );
// カテゴリー(投稿IDで代用)
this.#categoryBtns.forEach( ( btn ) => {
btn.addEventListener( 'click', () => {
this.#categoryBtns.forEach( ( b ) =>
b.classList.remove( 'is-active' )
);
btn.classList.add( 'is-active' );
this.#applyFilters();
} );
} );
}
#applyFilters() {
const query = this.#searchInput?.value.trim().toLowerCase() ?? '';
const activeBtn = document.querySelector( '.category-btn.is-active' );
const userId = activeBtn?.dataset.userId ?? 'all';
this.#filtered = this.#posts
// ① ユーザーIDで絞り込む
.filter( ( post ) =>
userId === 'all' || String( post.userId ) === userId
)
// ② 検索ワードでタイトルを絞り込む
.filter( ( post ) =>
query === '' || post.title.toLowerCase().includes( query )
);
this.#render();
}
#render() {
if ( this.#filtered.length === 0 ) {
this.#container.innerHTML = `
<div class="empty-state">
<p>該当する投稿がありません</p>
</div>
`;
return;
}
this.#container.innerHTML = this.#filtered
.map( ( post ) => `
<article class="card" data-id="${ post.id }">
<div class="card-header">
<span class="user-badge">User ${ post.userId }</span>
<span class="post-id">#${ post.id }</span>
</div>
<h2 class="card-title">${ post.title }</h2>
<p class="card-body">${ post.body }</p>
</article>
` )
.join( '' );
}
#showLoading() {
this.#container.innerHTML = `
<div class="loading">
<div class="spinner"></div>
<p>読み込み中...</p>
</div>
`;
}
#showError() {
this.#container.innerHTML = `
<div class="error-state">
<p>データの取得に失敗しました</p>
<button onclick="location.reload()">再読み込み</button>
</div>
`;
}
// 合計件数を取得する
get count() {
return this.#filtered.length;
}
}
const postList = new PostList( '#post-list' );
8. 実践チェックリスト
✓ forEach で配列の要素を1件ずつ処理してコンソールに表示できるか
✓ map で配列の要素をHTML文字列に変換して join('') でつなげられるか
✓ innerHTML に代入してDOMに表示できるか
✓ filter で条件に合う要素だけを絞り込んで表示できるか
✓ filter と map をチェーンして「絞り込み→HTML変換→表示」の流れが書けるか
✓ 検索フォームの input イベントで filter を使ったリアルタイム検索ができるか
✓ find でIDをキーに特定の要素を取り出せるか
✓ sort を使うときは元の配列を変更しないようにスプレッド構文でコピーしているか
まとめ
今回はJavaScriptで配列の要素を表示する方法を解説しました。ポイントをまとめると:
forEachは配列の各要素を1件ずつ処理するときに使う・戻り値がなく副作用のある処理に向いているmapは配列の各要素を変換して新しい配列を作る・map().join('')でHTMLを生成してDOMに挿入するのが実務の定番パターンfilterは条件に合う要素だけを抽出して新しい配列を作る・カテゴリー絞り込みや検索機能の実装に使うfilterとmapをチェーンして「絞り込み→変換→連結」の流れで書くとシンプルなコードになるfindは条件に合う最初の要素を1件だけ取得する・IDで特定のオブジェクトを探すときに使うsomeは1つでも条件を満たす要素があるか・everyはすべての要素が条件を満たすかを確認するreduceは配列を1つの値に集約する・合計・グループ化などに使うsortは元の配列を直接変更する破壊的メソッドのためスプレッド構文でコピーしてから使う習慣をつける
次の記事では、JavaScriptのデータ操作として配列メソッドのmap・filter・reduceをさらに深く掘り下げます。実務でよく使うパターンを中心に、複雑なデータ処理もシンプルに書ける方法をお届けします。お楽しみに!
コメント