JavaScript 逆引き集 | map で配列変換

JavaScript JavaScript
スポンサーリンク

実務向け map テンプレート

// 1) 基本(値・インデックス・配列の3引数、thisArg対応)
const result = arr.map((v, i, a) => v * 2, /* thisArg */ undefined);

// 2) 型安全な整形(Optionalを防御)
const rows = (arr ?? []).map(u => ({
  id: String(u.id ?? ''),
  name: String(u.name ?? '').trim(),
  role: u.role ?? 'N/A',
}));

// 3) ネストを減らす(条件分岐は関数に逃がす)
const flags = arr.map(v => checkFlag(v));

// 4) 事前容量確保した派生(TypedArrayなど)
const out = new Array(arr.length);
arr.map((v, i) => (out[i] = transform(v)));

// 5) キー生成・ラベル整形
const labels = items.map(it => `${it.item} x${it.qty}`);

// 6) Map/Set/NodeList を配列化してから map
const nodes = Array.from(document.querySelectorAll('li')).map(li => li.textContent);
JavaScript

map は配列の各要素に関数を一度ずつ適用し、その戻り値から新しい配列を生成します。引数は element, index, array の3つと任意の thisArg を取り、疎配列の空スロットはコールバックが呼ばれません。


性能とセキュリティのベストプラクティス

  • 用途に合う関数選択:
    • 変換は map、抽出は filter、集約は reduce。副作用中心なら forEach、早期終了や逐次 async は for/for…of。
  • 関数割り当ての削減:
    • ホットパスでは無名関数を毎回生成せず、外で定義して再利用。
  • 破壊的操作禁止:
    • map は非破壊。走査中に元配列へ splice/push などは避ける。必要なら新配列に書き出す。
  • 巨大入力の分割:
    • 数十万件規模はチャンク/ストリーミング。UI は仮想リストで描画を分割。
  • 非同期の誤用回避:
    • await arr.map(async ...) は期待通り逐次にならない。並列は Promise.all、逐次は for...of + await
  • 入力検証とエスケープ:
    • 外部入力は型検証し、文字列を HTML/SQL に渡す際は必ずエスケープ/パラメータ化でインジェクション対策。
  • 疎配列の挙動理解:
    • new Array(10) の空要素にはコールバックが呼ばれないので、必要なら初期化してから map。

React / Vue / Node の実務テンプレート

React

// 1) リスト描画(安定 key)
function UserList({ users }) {
  return (
    <ul>
      {users.map(u => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

// 2) 重前処理はロジック、描画は map
function Orders({ orders }) {
  const rows = orders.map(o => {
    const qty = Math.max(0, o.qty);
    const price = Math.max(0, o.price);
    const total = qty * price;
    return { id: o.id, label: `${o.item} x${qty}`, total, expensive: total >= 20000 };
  });

  return (
    <section>
      {rows.map(r => (
        <article key={r.id} className={r.expensive ? 'highlight' : ''}>
          <header>{r.label}</header>
          <div{r.total.toLocaleString()}</div>
        </article>
      ))}
    </section>
  );
}
JSX
  • ラベル: key は安定 ID。インデックスは並べ替えで破綻しやすい。

Vue

<template>
  <ul>
    <li v-for="u in viewUsers" :key="u.id">
      {{ u.name }} — {{ u.role ?? 'N/A' }}
    </li>
  </ul>
</template>

<script setup>
import { computed } from 'vue';

const props = defineProps({ users: { type: Array, required: true } });

const viewUsers = computed(() =>
  (props.users ?? []).map(u => ({
    id: u.id,
    name: (u.name ?? '').trim(),
    role: u.role ?? 'N/A',
  }))
);
</script>
HTML
  • 前処理: computed で map し、UI は v-for で宣言的に。

Node

// 1) 並列実行(API制限を守るなら p-limit 相当で制御)
async function saveAll(rows, send) {
  const tasks = rows.map(r => send(r));
  await Promise.all(tasks);
}

// 2) 逐次(順番保証が必要なとき)
async function saveSequential(rows, send) {
  for (const r of rows) await send(r);
}
TypeScript
  • 制御: レート/接続上限がある場合は並列数制限を設ける。

ステップバイステップ具体実装(注文配列の変換・表示・保存)

1. 型と検証

type Order = { id: string; item: string; qty: number; price: number; tags?: string[] };

export function isOrder(x: any): x is Order {
  return x
    && typeof x.id === 'string'
    && typeof x.item === 'string'
    && Number.isFinite(x.qty)
    && Number.isFinite(x.price);
}
TypeScript

2. map で前処理(負値防御・整形)

export function toOrderRows(orders) {
  const src = Array.isArray(orders) ? orders : [];
  return src
    .filter(isOrder) // 不正要素は落とす
    .map(o => {
      const qty = Math.max(0, o.qty);
      const price = Math.max(0, o.price);
      const total = qty * price;
      return {
        id: o.id,
        label: `${o.item} x${qty}`,
        total,
        expensive: total >= 20000,
        tags: Array.isArray(o.tags) ? o.tags.filter(Boolean) : [],
      };
    });
}
JavaScript

3. React で表示

function OrderList({ orders }) {
  const rows = toOrderRows(orders);
  return (
    <section>
      {rows.map(r => (
        <article key={r.id} className={r.expensive ? 'highlight' : ''}>
          <header>{r.label}</header>
          <div{r.total.toLocaleString()}</div>
          {!!r.tags.length && <small>Tags: {r.tags.join(', ')}</small>}
        </article>
      ))}
    </section>
  );
}
JSX

4. Vue で表示

<template>
  <section>
    <article v-for="r in rows" :key="r.id" :class="{ highlight: r.expensive }">
      <header>{{ r.label }}</header>
      <div>¥{{ r.total.toLocaleString() }}</div>
      <small v-if="r.tags?.length">Tags: {{ r.tags.join(', ') }}</small>
    </article>
  </section>
</template>

<script setup>
import { computed } from 'vue';
import { toOrderRows } from './orders.js';

const props = defineProps({ orders: { type: Array, required: true } });
const rows = computed(() => toOrderRows(props.orders));
</script>
HTML

5. Node.js で保存(並列 or 逐次)

import { toOrderRows } from './orders.js';

// 並列(レート制限に注意)
export async function saveOrdersParallel(db, orders, limit = 10) {
  const rows = toOrderRows(orders);
  // 簡易並列制御(同時 limit 件)
  let i = 0;
  async function worker() {
    while (i < rows.length) {
      const idx = i++;
      const r = rows[idx];
      await db.insert({ id: r.id, label: r.label, total: r.total, tags: r.tags });
    }
  }
  await Promise.all(Array.from({ length: limit }, worker));
}

// 逐次(順序保証)
export async function saveOrdersSequential(db, orders) {
  for (const r of toOrderRows(orders)) {
    await db.insert({ id: r.id, label: r.label, total: r.total, tags: r.tags });
  }
}
JavaScript

小さなライブラリ推奨とミニユーティリティ

  • 並列数制御:
    • p-limit 相当の軽量実装で API 呼び出しを制御。スループットと安定性のバランスを取る。
  • 部分導入:
    • lodash-es は個別 import(map, chunk などだけ)でバンドルを軽量化。
  • 再利用ヘルパ:
    • よく使う整形パターンをユーティリティに抽出。
// compactMap(null/undefined を除外して変換)
export function compactMap(arr, fn) {
  const out = [];
  for (const v of arr ?? []) {
    const r = fn(v);
    if (r != null) out.push(r);
  }
  return out;
}

// enumerateMap(インデックスも使いたい)
export function enumerateMap(arr, fn) {
  return (arr ?? []).map((v, i) => fn(v, i));
}
JavaScript

クイックリファレンス

  • map は新配列を返す/元配列は変更しない。
  • 引数: (element, index, array)thisArg を取れる。
  • 疎配列は空スロットをスキップする。
  • 副作用ではなく「変換」に使う。
タイトルとURLをコピーしました