



WithCodeMedia-1-pc
WithCodeMedia-2-pc
WithCodeMedia-3-pc
WithCodeMedia-4-pc




WithCodeMedia-1-sp
WithCodeMedia-2-sp
WithCodeMedia-3-sp
WithCodeMedia-4-sp









生徒forEachとmapって何が違うんですか?速さも違うんですか?
ペン博士いい質問だ。速さも用途もちゃんと違う。実際に計測したデータを見せながら説明しよう。
JavaScriptで配列を扱うとき、for文・forEach・map・reduce・for...of・whileなど、さまざまな手法が存在する。「どれを使えばいいか」は書き方の好みだけでなく、パフォーマンス・可読性・意図の明確さという3軸で選ぶべきだ。この記事では、実際にNode.js環境で手元計測した数値を示しながら、各手法の特性と使い分けを徹底解説する。
まずベンチマークの前提を明示する。計測結果はあくまで手元環境での目安であり、実行環境・JSエンジンのバージョン・データの内容によって変動する。
| 項目 | 値 |
|---|---|
| OS | macOS 14.5 (Apple M2) |
| Node.js | v22.2.0 |
| 計測方法 | performance.now() で前後を挟み、10回の平均を取る |
| 配列サイズ | 100万要素(1,000,000件)の数値配列 |
| ウォームアップ | 計測前に同一処理を3回空回し(JITウォームアップ) |
以下が今回使ったベンチマークスクリプトの全体だ。コピーして手元で動かせる。
// benchmark.js Node.js v22で動作確認済み
const { performance } = require('perf_hooks');
const SIZE = 1_000_000;
const arr = Array.from({ length: SIZE }, (_, i) => i + 1);
function bench(label, fn, warmup = 3, runs = 10) {
// ウォームアップ(JIT最適化を安定させる)
for (let w = 0; w < warmup; w++) fn();
const times = [];
for (let r = 0; r < runs; r++) {
const t0 = performance.now();
fn();
times.push(performance.now() - t0);
}
const avg = times.reduce((a, b) => a + b, 0) / times.length;
const min = Math.min(...times);
const max = Math.max(...times);
console.log(
`${label.padEnd(20)} avg=${avg.toFixed(2)}ms min=${min.toFixed(2)}ms max=${max.toFixed(2)}ms`
);
return avg;
}
// ─── 各手法の実装 ───────────────────────────────────────────────────
// 1) for(伝統的なインデックスループ)
bench('for', () => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
});
// 2) while
bench('while', () => {
let sum = 0;
let i = arr.length;
while (i--) {
sum += arr[i];
}
return sum;
});
// 3) for...of
bench('for...of', () => {
let sum = 0;
for (const v of arr) {
sum += v;
}
return sum;
});
// 4) forEach
bench('forEach', () => {
let sum = 0;
arr.forEach(v => { sum += v; });
return sum;
});
// 5) reduce
bench('reduce', () => arr.reduce((acc, v) => acc + v, 0));
// 6) map(副作用目的での誤用を計測)
bench('map (×副作用)', () => {
let sum = 0;
arr.map(v => { sum += v; }); // 本来の用途ではない使い方
return sum;
});
このスクリプトをNode.jsで実行すると、各手法の平均・最小・最大実行時間が出力される。以下に手元での実測結果をまとめた。
手元環境(上記のM2 Mac + Node.js v22.2.0)で計測した結果が以下だ。数値は10回実行の平均ミリ秒。
| 手法 | 平均実行時間(ms) | 最速比(for基準) | 戻り値 | 可読性 | 主な用途 |
|---|---|---|---|---|---|
| for(インデックス) | 2.1 | 1.00×(基準) | なし | 中 | 汎用・高速処理 |
| while(逆順) | 2.0 | 0.95× | なし | 低 | 最速を狙う場面 |
| for…of | 3.4 | 1.62× | なし | 高 | 可読性重視の繰り返し |
| forEach | 4.8 | 2.29× | なし(undefined) | 高 | 副作用ありの繰り返し |
| reduce | 3.2 | 1.52× | 集計値 | 中〜高 | 合計・変換・グループ化 |
| map(副作用目的) | 5.3 | 2.52× | 新配列(不要) | 低 | 誤用→使うべきでない |
「最速はwhileのインデックス逆順ループ」という結果だった。ただし差は微々たるもので、100万件の処理でも2〜5ms程度の差にすぎない。普通のWebアプリ開発では速度差よりも可読性・意図の明確さを優先すべきだ。
for文はJavaScriptで最も基本的なループ構文だ。インデックスを直接操作できるため、途中でbreakする・特定インデックスだけ処理する・逆順ループなど、細かい制御が必要な場面で強い。
// 基本的なfor文
const fruits = ['apple', 'banana', 'cherry'];
for (let i = 0; i < fruits.length; i++) {
console.log(`${i}: ${fruits[i]}`);
}
// 0: apple
// 1: banana
// 2: cherry
// 逆順ループ
for (let i = fruits.length - 1; i >= 0; i--) {
console.log(fruits[i]);
}
// cherry → banana → apple
// 特定インデックスだけスキップ
for (let i = 0; i < fruits.length; i++) {
if (i === 1) continue; // banana をスキップ
console.log(fruits[i]);
}
// apple, cherry
// 途中でbreak
for (let i = 0; i < fruits.length; i++) {
if (fruits[i] === 'banana') break;
console.log(fruits[i]);
}
// apple だけ出力
わずかでも速度を稼ぎたいなら、arr.lengthをループ外でキャッシュするテクニックがある。ただし現代のV8エンジンはほぼ自動最適化するため、劇的な差は出ない。
// length をキャッシュ(古典的最適化)
const arr = new Array(1_000_000).fill(1);
const len = arr.length; // ← キャッシュ
let sum = 0;
for (let i = 0; i < len; i++) {
sum += arr[i];
}
// 現代のNode.jsでは上記とほぼ同速だが、
// 明示的に書くとコードの意図が明確になる利点はある
while文は条件が真の間ループし続ける。逆順ループ(デクリメント)と組み合わせると、i--が0になった瞬間にfalsyになる性質を利用でき、若干高速になる。
// 通常のwhileループ
const nums = [10, 20, 30, 40, 50];
let i = 0;
while (i < nums.length) {
console.log(nums[i]);
i++;
}
// 逆順while(ベンチで最速だったパターン)
let j = nums.length;
let sum = 0;
while (j--) {
sum += nums[j];
}
console.log(sum); // 150
// do...while(最低1回は実行される)
let count = 0;
do {
console.log(`count: ${count}`);
count++;
} while (count < 3);
// count: 0
// count: 1
// count: 2
whileは可読性が低くなりやすい。速度よりも可読性が重要なアプリコードでは、forやfor…ofを使うほうが保守しやすい。whileはパフォーマンスクリティカルな処理(ゲームエンジン・音声処理など)で意識的に使うくらいにとどめよう。
for...ofはES2015で追加された構文で、配列だけでなく、Mapや Set・文字列・NodeListなど、イテラブルなオブジェクト全般に使える。インデックスが不要な場合はfor文よりも意図が明確になる。
// 配列のfor...of
const colors = ['red', 'green', 'blue'];
for (const color of colors) {
console.log(color);
}
// entriesでインデックスも取得
for (const [index, color] of colors.entries()) {
console.log(`${index}: ${color}`);
}
// 0: red, 1: green, 2: blue
// Setに対して
const set = new Set([1, 2, 3, 2, 1]);
for (const v of set) {
console.log(v); // 1, 2, 3(重複除去済み)
}
// Mapに対して
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, val] of map) {
console.log(`${key}=${val}`);
}
// 文字列に対して(文字単位で処理)
for (const char of 'hello') {
console.log(char); // h, e, l, l, o
}
// breakも使える
for (const num of [1, 2, 3, 4, 5]) {
if (num === 3) break;
console.log(num); // 1, 2
}
for…in は配列に使ってはいけない。for...inはオブジェクトの列挙可能なプロパティ名(キー)をイテレートするもので、配列に使うとプロトタイプチェーン上のプロパティも拾ってしまうリスクがある。
// for...in はオブジェクトのキー列挙に使う
const person = { name: 'Alice', age: 25, city: 'Tokyo' };
for (const key in person) {
console.log(`${key}: ${person[key]}`);
}
// name: Alice
// age: 25
// city: Tokyo
// ❌ 配列にfor...inを使う(やってはいけない)
const arr = [10, 20, 30];
Array.prototype.customMethod = function() {}; // プロトタイプ拡張の例
for (const key in arr) {
console.log(key); // "0", "1", "2", "customMethod" ← 意図しないキーが出る!
}
// ✅ 配列にはfor...ofを使う
for (const val of arr) {
console.log(val); // 10, 20, 30(正常)
}
forEachはES5から使える配列メソッドで、コールバック関数を各要素に対して実行する。戻り値は常にundefinedであり、breakで途中終了はできない。副作用(DOM操作・ログ出力・外部状態の変更)を伴う処理に適している。
// 基本的なforEach
const scores = [85, 92, 78, 95, 88];
scores.forEach((score, index, array) => {
// 第2引数: インデックス
// 第3引数: 元の配列
console.log(`${index + 1}位: ${score}点`);
});
// DOM操作の例(ブラウザ環境)
const buttons = document.querySelectorAll('.btn');
buttons.forEach(btn => {
btn.addEventListener('click', () => {
btn.classList.toggle('active');
});
});
// オブジェクト配列の処理
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
];
const names = [];
users.forEach(user => {
names.push(user.name); // 副作用: 外部配列に追加
});
console.log(names); // ['Alice', 'Bob']
// ※この場合はmapを使うほうが意図が明確
// ⚠️ breakはできない(例外を使うハックがあるが非推奨)
scores.forEach(score => {
if (score > 90) return; // returnはcontinueと同義(ループは止まらない)
console.log(score); // 85, 78, 88 が出力される
});
forEachの中でawaitは効かない。これはJavaScript開発者がよくハマる落とし穴の一つだ。
// ❌ forEach + async/await は正しく動かない
const ids = [1, 2, 3];
// これは「並列実行」になってしまい、完了を待てない
ids.forEach(async (id) => {
const data = await fetch(`/api/user/${id}`);
console.log(await data.json());
});
console.log('done'); // ← 全fetchが終わる前に出力される!
// ✅ for...of + await が正しい(直列実行)
async function fetchSequential() {
for (const id of ids) {
const data = await fetch(`/api/user/${id}`);
console.log(await data.json()); // 1件ずつ順番に処理
}
console.log('done'); // ← 全件処理後に出力
}
// ✅ Promise.all + map で並列実行(高速)
async function fetchParallel() {
const results = await Promise.all(
ids.map(id => fetch(`/api/user/${id}`).then(r => r.json()))
);
console.log(results); // 全件まとめて取得
}
mapは配列の各要素を変換し、同じ長さの新しい配列を返す。元の配列は変更されない(イミュータブル)。データの変換・加工に使う。
// 数値を2倍に変換
const nums = [1, 2, 3, 4, 5];
const doubled = nums.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(nums); // [1, 2, 3, 4, 5] ← 元は変わらない
// オブジェクト配列からプロパティを抽出
const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 },
{ id: 3, name: 'Carol', age: 22 },
];
const names = users.map(u => u.name);
console.log(names); // ['Alice', 'Bob', 'Carol']
// オブジェクトを別の形に変換(データ整形)
const formatted = users.map(u => ({
label: `${u.name}(${u.age}歳)`,
value: u.id,
}));
// [{ label: 'Alice(25歳)', value: 1 }, ...]
// インデックスを使った変換
const withRank = users.map((u, i) => ({
rank: i + 1,
...u,
}));
// [{ rank: 1, id: 1, name: 'Alice', ... }, ...]
// Reactでのリストレンダリング(よく使うパターン)
// const elements = items.map(item => <li key={item.id}>{item.name}</li>);
ベンチでも最も遅かった「mapを副作用目的に使うパターン」は、パフォーマンスと可読性の両面で問題がある。
// ❌ mapを副作用に使う(よくある誤用)
const results = [];
[1, 2, 3].map(n => {
results.push(n * 2); // 副作用! 戻り値(新配列)を使っていない
});
// mapが生成した配列は捨てられているので無駄
// ✅ 副作用 → forEach を使う
[1, 2, 3].forEach(n => {
results.push(n * 2);
});
// ✅ 変換 → mapの戻り値を使う
const doubled = [1, 2, 3].map(n => n * 2);
// なぜ誤用がいけないか
// 1. mapは新配列を生成するのでメモリ無駄
// 2. コードを読む人が「戻り値を使っているはず」と思って混乱する
// 3. ベンチでもforEachより遅い
reduceは配列を左から右へ処理し、単一の値(数値・オブジェクト・配列など何でも)に集約する最も汎用的な高階関数だ。慣れるまで難しく見えるが、合計・グループ化・フラット化など幅広い処理を表現できる。
// 合計を求める(基本)
const nums = [1, 2, 3, 4, 5];
const sum = nums.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 15
// 引数の意味:
// accumulator: 前の計算結果(初回は初期値=0)
// current : 現在の要素
// 0 : 初期値
// 最大値を求める
const max = nums.reduce((acc, v) => (v > acc ? v : acc), -Infinity);
console.log(max); // 5
// 配列をオブジェクトに変換(グループ化)
const people = [
{ name: 'Alice', dept: 'dev' },
{ name: 'Bob', dept: 'design' },
{ name: 'Carol', dept: 'dev' },
];
const grouped = people.reduce((acc, person) => {
const key = person.dept;
if (!acc[key]) acc[key] = [];
acc[key].push(person.name);
return acc;
}, {});
console.log(grouped);
// { dev: ['Alice', 'Carol'], design: ['Bob'] }
// フラット化(flat()が使えないケース向け)
const nested = [[1, 2], [3, 4], [5]];
const flat = nested.reduce((acc, arr) => acc.concat(arr), []);
console.log(flat); // [1, 2, 3, 4, 5]
// 単語の出現回数を数える
const words = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'];
const counts = words.reduce((acc, word) => {
acc[word] = (acc[word] || 0) + 1;
return acc;
}, {});
console.log(counts);
// { apple: 3, banana: 2, cherry: 1 }
reduceRightは右から左へ処理する。関数の合成(pipe/compose)でよく使われる。
// reduceRight: 右から左へ
const arr = ['a', 'b', 'c'];
const result = arr.reduceRight((acc, v) => acc + v, '');
console.log(result); // 'cba'
// 関数合成(compose)の実装
const double = x => x * 2;
const addOne = x => x + 1;
const square = x => x * x;
// compose: 右から左へ関数を適用
const compose = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x);
const transform = compose(double, addOne, square);
console.log(transform(3)); // square(3)=9 → addOne(9)=10 → double(10)=20
filterは条件を満たす要素だけを集めた新しい配列を返す。元の配列は変わらない。
const products = [
{ name: 'MacBook', price: 200000, inStock: true },
{ name: 'iPhone', price: 130000, inStock: false },
{ name: 'iPad', price: 80000, inStock: true },
{ name: 'AirPods', price: 28000, inStock: true },
];
// 在庫ありの商品だけ
const available = products.filter(p => p.inStock);
console.log(available.length); // 3
// 価格が10万円以下の在庫あり商品
const affordable = products.filter(p => p.inStock && p.price <= 100000);
// [{ name: 'iPad', ... }, { name: 'AirPods', ... }]
// map と filter のチェーン
const names = products
.filter(p => p.inStock)
.map(p => p.name);
console.log(names); // ['MacBook', 'iPad', 'AirPods']
findは条件を満たす最初の要素を返す。見つからなければundefined。findIndexはそのインデックスを返す。
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' },
];
// IDで検索
const user = users.find(u => u.id === 2);
console.log(user); // { id: 2, name: 'Bob' }
// インデックスで検索
const idx = users.findIndex(u => u.name === 'Carol');
console.log(idx); // 2
// 見つからない場合
const notFound = users.find(u => u.id === 99);
console.log(notFound); // undefined
// findIndex が -1 を返す場合
const notFoundIdx = users.findIndex(u => u.id === 99);
console.log(notFoundIdx); // -1
どの手法を使うか迷ったら、以下の判断フローを参考にしよう。
| やりたいこと | 使う手法 | 理由 |
|---|---|---|
| すべての要素を処理(副作用あり) | forEach | 戻り値不要・意図が明確 |
| 配列を別の値に変換 | map | 新配列を返す・元は不変 |
| 条件で絞り込む | filter | 条件一致のみ新配列 |
| ひとつの値に集約 | reduce | 合計・グループ化など |
| 最初の一致を探す | find / findIndex | 全件スキャン不要・高速 |
| 高速な繰り返し(速度優先) | for / while | オーバーヘッドが最小 |
| イテラブル全般(Map・Set等) | for…of | 汎用性が高い |
| 非同期処理を順番に実行 | for…of + await | forEachではawaitが効かない |
| 非同期処理を並列実行 | Promise.all + map | 全件同時に走らせて高速 |
また、複数の操作をチェーンする場合はfilter → map → reduceの順で考えると整理しやすい。
// 実践的なチェーン例:
// 「在庫ありの商品の価格合計を求める」
const products = [
{ name: 'A', price: 1000, inStock: true },
{ name: 'B', price: 2000, inStock: false },
{ name: 'C', price: 1500, inStock: true },
{ name: 'D', price: 3000, inStock: true },
];
const totalAvailable = products
.filter(p => p.inStock) // 在庫ありに絞る
.map(p => p.price) // 価格だけ取り出す
.reduce((sum, price) => sum + price, 0); // 合計
console.log(totalAvailable); // 5500
// 大量データならreduce一本にまとめた方が高速(一度だけ走査)
const totalFast = products.reduce((sum, p) => {
return p.inStock ? sum + p.price : sum;
}, 0);
console.log(totalFast); // 5500(同じ結果)
先述の通り、mapの戻り値を無視するのはメモリの無駄で、コードの可読性も下げる。副作用が目的なら必ずforEachを使おう。
// ❌ よくある「とりあえずPromise.allをforEachに入れる」誤り
const ids = [1, 2, 3];
// これは動くが、直列か並列かが不明瞭
const promises = [];
ids.forEach(id => {
promises.push(fetch(`/api/${id}`));
});
await Promise.all(promises); // 動くが、mapを使えばシンプル
// ✅ シンプルな書き方
const results = await Promise.all(ids.map(id => fetch(`/api/${id}`)));
// ❌ 配列の合計をreduceで書く(冗長ではないが…)
const sum = [1, 2, 3].reduce((a, b) => a + b);
// これは問題ないが、下記の方が読みやすい場面もある
// ✅ 単純な合計ならfor...ofやforEachも明確
let total = 0;
for (const n of [1, 2, 3]) total += n;
// reduceが本領を発揮するのは「集約の形が複雑な時」
// 例:ネストしたデータのフラット化、グループ化、
// 複数の計算を同時に行う場合など
100万件を超えるような配列に対してfilter → map → reduceをチェーンすると、配列を3回走査することになる。大量データはreduceひとつにまとめるか、for文で一度に処理するほうが効率的だ。
const LARGE = new Array(1_000_000).fill(0).map((_, i) => ({
value: i,
active: i % 2 === 0,
}));
// ❌ 100万件を3回走査(非効率)
const result1 = LARGE
.filter(x => x.active)
.map(x => x.value * 2)
.reduce((s, v) => s + v, 0);
// ✅ 100万件を1回だけ走査(高効率)
const result2 = LARGE.reduce((s, x) => {
if (x.active) s += x.value * 2;
return s;
}, 0);
console.log(result1 === result2); // true(同じ結果)
実際のアプリで10万件以上のデータを扱う場面では、チェーンの書き方一つでパフォーマンスが大きく変わる。以下に実測を交えたパターンを示す。
// 100,000件のユーザーデータから「アクティブで30歳未満」を抽出して名前リストを作る
const users = Array.from({ length: 100_000 }, (_, i) => ({
id: i,
name: `User${i}`,
age: Math.floor(Math.random() * 60) + 18,
active: Math.random() > 0.3,
}));
// パターン1: filter → map チェーン(2回走査)
console.time('chain');
const result1 = users
.filter(u => u.active && u.age < 30)
.map(u => u.name);
console.timeEnd('chain');
// 手元計測: 約 8.2ms
// パターン2: reduce 一本(1回走査)
console.time('reduce');
const result2 = users.reduce((acc, u) => {
if (u.active && u.age < 30) acc.push(u.name);
return acc;
}, []);
console.timeEnd('reduce');
// 手元計測: 約 5.4ms(約34%高速)
// パターン3: for文(最速)
console.time('for');
const result3 = [];
for (let i = 0; i < users.length; i++) {
const u = users[i];
if (u.active && u.age < 30) result3.push(u.name);
}
console.timeEnd('for');
// 手元計測: 約 4.1ms(チェーンの約2倍速)
// ※ 件数が少ない場合(1,000件程度)は差は無視できるほど小さい
SetとArray.fromの組み合わせが最もシンプルだが、条件付き重複削除(IDベースなど)にはreduceやMapが有効だ。
// シンプルな重複削除(プリミティブ値)
const nums = [1, 2, 3, 2, 1, 4, 3];
// パターン1: Set(最もシンプル・高速)
const unique1 = [...new Set(nums)];
// [1, 2, 3, 4]
// パターン2: filter + indexOf(古典的・遅い)
const unique2 = nums.filter((v, i) => nums.indexOf(v) === i);
// [1, 2, 3, 4] ← 同じ結果だが O(n²) で遅い
// オブジェクト配列のID重複削除
const items = [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
{ id: 1, name: 'A duplicate' }, // id:1 の重複
{ id: 3, name: 'C' },
];
// パターン1: Map を使った重複削除(高速・O(n))
const uniqueById = [...new Map(items.map(item => [item.id, item])).values()];
// [{ id:1, name:'A' }, { id:2, name:'B' }, { id:3, name:'C' }]
// パターン2: reduce で重複削除
const uniqueByIdReduce = items.reduce((acc, item) => {
if (!acc.find(x => x.id === item.id)) acc.push(item);
return acc;
}, []);
// ※ find が内側でO(n)なので全体O(n²)。Mapの方が速い
JavaScriptのArray.prototype.sortはES2019以降安定ソート(stable sort)が保証された。ソートの書き方と注意点を示す。
// 数値ソート(比較関数が必要)
const nums = [10, 1, 21, 2];
// ❌ デフォルトのsortは文字列順(バグの元)
console.log([...nums].sort());
// [1, 10, 2, 21] ← 文字列として比較!
// ✅ 比較関数を渡す
console.log([...nums].sort((a, b) => a - b)); // 昇順
// [1, 2, 10, 21]
console.log([...nums].sort((a, b) => b - a)); // 降順
// [21, 10, 2, 1]
// オブジェクト配列のソート
const users = [
{ name: 'Carol', age: 22 },
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 22 },
];
// ageで昇順ソート(同じageはname順で安定ソート)
users.sort((a, b) => {
if (a.age !== b.age) return a.age - b.age;
return a.name.localeCompare(b.name); // 文字列比較
});
// Carol(22) → Bob(22) → Alice(30) ではなく
// Bob(22) → Carol(22) → Alice(30) になる(name昇順)
// 日本語文字列のソート(localeCompare必須)
const items = ['東京', 'あいう', 'ABC', '大阪'];
items.sort((a, b) => a.localeCompare(b, 'ja'));
// 配列を特定サイズのチャンクに分割
function chunkArray(arr, size) {
const chunks = [];
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
console.log(chunkArray([1,2,3,4,5,6,7], 3));
// [[1,2,3], [4,5,6], [7]]
// reduceでも書ける
const chunk = (arr, n) =>
arr.reduce((acc, _, i) =>
i % n === 0 ? [...acc, arr.slice(i, i + n)] : acc, []);
// 2次元配列をフラット化
const matrix = [[1,2],[3,4],[5,6]];
console.log(matrix.flat()); // [1,2,3,4,5,6]
console.log(matrix.flatMap(x=>x)); // 同じ結果(map+flatを一度に)
// 配列のzip(2配列を組み合わせる)
const keys = ['a', 'b', 'c'];
const values = [1, 2, 3];
const zipped = keys.map((k, i) => [k, values[i]]);
// [['a',1], ['b',2], ['c',3]]
// Object.fromEntries でオブジェクトに変換
const obj = Object.fromEntries(zipped);
// { a: 1, b: 2, c: 3 }
Reactや状態管理(Redux等)では元の配列を変更しないイミュータブルな操作が必須だ。破壊的なメソッドと非破壊的なメソッドを整理する。
// ─── 破壊的メソッド(元の配列を変更する)───
const arr = [1, 2, 3, 4, 5];
arr.push(6); // 末尾追加 → arr=[1,2,3,4,5,6]
arr.pop(); // 末尾削除 → arr=[1,2,3,4,5]
arr.shift(); // 先頭削除 → arr=[2,3,4,5]
arr.unshift(1); // 先頭追加 → arr=[1,2,3,4,5]
arr.splice(2, 1); // インデックス2から1件削除 → arr=[1,2,4,5]
arr.sort(); // ソート(元を変更)
arr.reverse(); // 逆順(元を変更)
// ─── 非破壊的な代替(Reactなどで推奨)───
const original = [1, 2, 3, 4, 5];
// 末尾追加: スプレッド
const added = [...original, 6]; // [1,2,3,4,5,6]
// 先頭追加
const prepended = [0, ...original]; // [0,1,2,3,4,5]
// 特定インデックスを削除
const removed = original.filter((_, i) => i !== 2); // インデックス2を除く
// [1,2,4,5]
// 特定インデックスを更新
const updated = original.map((v, i) => i === 2 ? 99 : v);
// [1,2,99,4,5]
// 特定インデックスに挿入
const inserted = [
...original.slice(0, 2),
99,
...original.slice(2),
];
// [1,2,99,3,4,5]
// ソート(元を変更しない)
const sorted = [...original].sort((a, b) => b - a); // 降順
// [5,4,3,2,1](originalは変わらない)
一般的なWebアプリ開発では可読性が高いforEachやfor…ofを推奨する。速度差は100万件で数ミリ秒程度であり、実用上の問題にはほとんどならない。ゲームエンジンや音声処理など、毎フレーム数万回以上ループが走る場面ではforやwhileを検討しよう。
判断は簡単だ。変換した新しい配列が必要 → map、副作用だけ起こしたい → forEach。この二択で迷うことはなくなる。
最初は「合計を求める」だけから始めよう。reduce((acc, val) => acc + val, 0)を手で書けるようになったら、次に「グループ化」に挑戦する。無理にreduceで全てを書く必要はなく、forループやmapで読みやすく書けるなら、そちらを選ぶのが正解だ。
for...inはオブジェクトのキー列挙用で、プロトタイプチェーン上のプロパティも拾う可能性がある。配列にはfor...ofかforEachを使おう。
はい。ChromeのV8エンジンはNode.jsと同じエンジンなので傾向は近いが、Firefox(SpiderMonkey)やSafari(JavaScriptCore)では挙動が異なる。クリティカルな処理は実際に動かす環境でベンチマークすることを強く勧める。
// some: 条件を満たす要素が「1つでも」あるか
const hasAdult = [15, 18, 20].some(age => age >= 18); // true
// every: 「全て」の要素が条件を満たすか
const allAdult = [18, 20, 25].every(age => age >= 18); // true
// flatMap: map + flat を一度に
const sentences = ['Hello World', 'foo bar'];
const words = sentences.flatMap(s => s.split(' '));
console.log(words); // ['Hello', 'World', 'foo', 'bar']
配列の繰り返しは「目的」で手法を選べ
変換 → map、副作用 → forEach、集約 → reduce、条件絞り → filter
速度差は100万件で数msレベル。普段は可読性優先でOK
JavaScriptの配列処理を正しく使い分けられると、コードの意図が明確になり、チーム開発でもバグが減る。まずは今日のコードで「mapを副作用に使っていないか」だけチェックしてみよう。
| 指標 | 実績 | 調査対象・期間 |
|---|---|---|
| 案件獲得率 | 97.9% | 受講生アンケート/2025年1月〜12月 |
| 受講料金完済率 | 84.7% | 受講生アンケート/2025年6月〜12月 |
| 運営体制 | Web制作会社が直接運営 | 現役制作会社が直接指導 |
※受講生アンケートに基づく自社調査結果(2025年1月〜2025年12月実施)
※受講生アンケートに基づく自社調査結果(2025年6月〜2025年12月実施)

副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?
未経験でも心配することはありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!
WithCodeでWeb制作を習得後、フリーランスエンジニアとして活動。HTML/CSS・JavaScript・WordPress案件を中心に年間20件以上の制作実績を持つ。「難しい技術をわかりやすく」をモットーに、初心者〜中級者向けの技術記事を執筆。副業・フリーランス独立を目指す方に向けた情報発信に注力している。
関連する記事はまだ見つかりませんでした。
公式サイト より
今すぐ
無料カウンセリング
を予約!