JavaScript Array.from()の実践的な使い方|業務で役立つパターン集

JavaScript

JavaScript Array.from()の実践的な使い方|業務で役立つパターン集

JavaScriptを扱う開発者なら、配列操作は日常的な業務です。その中でもArray.from()は非常に便利なメソッドですが、意外とその活用方法を完全に理解していない人も多いのではないでしょうか。本記事では、Array.from()の基本から実務で実際に使えるパターンまで、実装例を交えて解説します。

Array.from()とは

Array.from()は、反復可能なオブジェクト(イテラブル)または配列風のオブジェクト(アレイライク)を、新しい配列に変換するメソッドです。文法は以下の通りです。

Array.from(arrayLike, mapFn, thisArg)

第一引数のarrayLikeが変換対象となるオブジェクト、第二引数のmapFnがマッピング関数(オプション)、第三引数のthisArgがコンテキスト指定(オプション)です。戻り値は新しい配列となります。

業務でのユースケース

実務におけるArray.from()の活用場面は多岐にわたります。最も一般的なケースを3つ紹介します。

1. DOM操作での活用

DOMを扱う際、document.querySelectorAll()document.getElementsByClassName()は配列風のオブジェクト(NodeList)を返します。これらに配列メソッドを適用する場合、Array.from()での変換が必要です。

2. 文字列の文字単位での分割

日本語を含む文字列では、スプレッド演算子やsplit()よりもArray.from()が正確に処理できる場合があります。

3. 数値範囲の生成

1から100までの連番配列など、特定範囲の数値配列を素早く生成する必要があります。

実装コード|DOM操作パターン

まず最も実務的なDOM操作の例を見てみましょう。

// ❌ 従来の方法(フォームの全inputを取得して値を収集)
const inputs = document.querySelectorAll('input[type=\"text\"]');
const values = [];
for (let i = 0; i < inputs.length; i++) {
  values.push(inputs[i].value);
}

// ✅ Array.from()を使った方法
const values = Array.from(document.querySelectorAll('input[type=\"text\"]'))
  .map(input => input.value);

// さらに実務的な例:フォーム検証
const getFormData = (formSelector) => {
  return Array.from(document.querySelectorAll(`${formSelector} [name]`))
    .reduce((acc, field) => {
      acc[field.name] = field.value;
      return acc;
    }, {});
};

// 使用例
const formData = getFormData('#userForm');
console.log(formData); // { username: '..', email: '..', ... }

このパターンは業務で頻出です。フォーム送信時にすべての入力値を取得する場面で非常に便利です。

実装コード|データ変換パターン

次に、APIレスポンスなどのデータを変換する実例を見ます。

// API返却データを想定
const apiResponse = {
  users: [
    { id: 1, name: 'Tanaka', age: 28 },
    { id: 2, name: 'Suzuki', age: 35 },
    { id: 3, name: 'Yamada', age: 42 }
  ]
};

// ❌ 従来の方法:配列を変換してからマッピング
const userNames = [];
for (const user of apiResponse.users) {
  userNames.push(user.name);
}

// ✅ Array.from()を使った方法
const userNames = Array.from(apiResponse.users, user => user.name);
// 結果: ['Tanaka', 'Suzuki', 'Yamada']

// さらに複雑な変換例:ユーザーオブジェクトを表示用フォーマットに変換
const displayUsers = Array.from(apiResponse.users, user => ({
  label: `${user.name}(${user.age}歳)`,
  value: user.id,
  isAdult: user.age >= 20
}));

console.log(displayUsers);
// [
//   { label: 'Tanaka(28歳)', value: 1, isAdult: true },
//   { label: 'Suzuki(35歳)', value: 2, isAdult: true },
//   { label: 'Yamada(42歳)', value: 3, isAdult: true }
// ]

Array.from()の第二引数でマッピングを行うことで、変換と配列化を一度に処理でき、コードがシンプルになります。

実装コード|配列生成パターン

特定の範囲や条件で配列を生成する実務的なパターンです。

// 1から100までの配列を生成
const numbers = Array.from({ length: 100 }, (_, i) => i + 1);

// テーブルの行データを生成(実務例:レポート生成)
const tableRows = Array.from({ length: 12 }, (_, month) => ({
  month: month + 1,
  sales: Math.floor(Math.random() * 1000000),
  target: 500000
}));

// ページネーション用の配列生成
const generatePageNumbers = (currentPage, totalPages) => {
  const maxVisible = 5;
  let start = Math.max(1, currentPage - Math.floor(maxVisible / 2));
  let end = Math.min(totalPages, start + maxVisible - 1);
  
  if (end - start < maxVisible - 1) {
    start = Math.max(1, end - maxVisible + 1);
  }
  
  return Array.from({ length: end - start + 1 }, (_, i) => start + i);
};

console.log(generatePageNumbers(5, 20)); 
// [3, 4, 5, 6, 7]

// グリッドレイアウト用のダミーデータ生成
const generateGridItems = (count) => {
  return Array.from({ length: count }, (_, i) => ({
    id: `item-${i + 1}`,
    title: `Item ${i + 1}`,
    image: `https://via.placeholder.com/300?text=Item${i + 1}`
  }));
};

TypeScript での実装例

型安全性が求められるプロジェクトではTypeScriptを使用します。Array.from()をTypeScriptで使う際の実装例を紹介します。

// ユーザー型の定義
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

interface UserDisplay {
  label: string;
  value: number;
}

// DOM要素からユーザーデータを抽出する関数
const extractUserFromDOM = (container: HTMLElement): User[] => {
  return Array.from(container.querySelectorAll('[data-user]'), (element) => {
    const dataset = (element as HTMLElement).dataset;
    return {
      id: parseInt(dataset.userId || '0'),
      name: dataset.userName || '',
      email: dataset.userEmail || '',
      age: parseInt(dataset.userAge || '0')
    };
  });
};

// ユーザーデータを表示形式に変換
const formatUsersForDisplay = (users: User[]): UserDisplay[] => {
  return Array.from(users, (user) => ({
    label: `${user.name} <${user.email}>`,
    value: user.id
  }));
};

// 使用例
const container = document.getElementById('userContainer') as HTMLElement;
const users = extractUserFromDOM(container);
const displayUsers = formatUsersForDisplay(users);

Python での参考実装

JavaScriptのArray.from()に相当するPythonでの実装パターンも参考までに示します。

from typing import List, Dict, Callable, TypeVar, Any

T = TypeVar('T')
U = TypeVar('U')

# Array.from()に相当する関数
def array_from(iterable: Any, map_fn: Callable[[Any, int], U] = None) -> List[U]:
    \"\"\"
    反復可能なオブジェクトを配列に変換(マッピング機能付き)
    JavaScriptのArray.from()に相当
    \"\"\"
    if map_fn is None:
        return list(iterable)
    return [map_fn(item, i) for i, item in enumerate(iterable)]

# 使用例1:辞書リストをユーザー表示用に変換
users = [
    {'id': 1, 'name': 'Tanaka', 'age': 28},
    {'id': 2, 'name': 'Suzuki', 'age': 35}
]

display_users = array_from(
    users,
    lambda user, i: f\"{user['name']}({user['age']}歳)\"
)
# 結果: ['Tanaka(28歳)', 'Suzuki(35歳)']

# 使用例2:範囲から配列を生成
numbers = array_from(
    range(1, 101),
    lambda x, i: x
)
# 結果: [1, 2, 3, ..., 100]

# 使用例3:複数の処理を組み合わせ
processed = array_from(
    range(1, 6),
    lambda x, i: {
        'index': i,
        'value': x,
        'squared': x ** 2
    }
)
# 結果: [{'index': 0, 'value': 1, 'squared': 1}, ...]

よくある応用パターン

ネストされたデータの平坦化

// 複数のカテゴリー配列を1つにまとめる
const categories = [
  { name: '電子機器', items: ['PC', 'スマートフォン'] },
  { name: '衣類', items: ['Tシャツ', 'ジーンズ'] }
];

// ❌ 従来の方法
const allItems = [];
for (const cat of categories) {
  allItems.push(...cat.items);
}

// ✅ Array.from()を使った方法
const allItems = Array.from(
  categories.reduce((acc, cat) => [...acc, ...cat.items], [])
);

// さらに実務的:flatMap相当の処理
const allItemsWithCategory = categories.flatMap(cat =>
  Array.from(cat.items, item => ({
    item,
    category: cat.name
  }))
);

重複排除と同時に変換

// ログデータから重複なしのユーザーIDリストを取得
const logs = [
  { userId: 101, action: 'login' },
  { userId: 102, action: 'purchase' },
  { userId: 101, action: 'logout' },
  { userId: 103, action: 'login' }
];

const uniqueUserIds = Array.from(
  new Set(logs.map(log => log.userId))
);
// 結果: [101, 102, 103]

// さらに実務的:重複排除と同時にユーザー情報を取得
const getUniqueUserData = (logs) => {
  const userMap = new Map();
  
  Array.from(logs, log => {
    if (!userMap.has(log.userId)) {
      userMap.set(log.userId, {
        userId: log.userId,
        actions: []
      });
    }
    userMap.get(log.userId).actions.push(log.action);
  });
  
  return Array.from(userMap.values());
};

console.log(getUniqueUserData(logs));
// [
//   { userId: 101, actions: ['login', 'logout'] },
//   { userId: 102, actions: ['purchase'] },
//   { userId: 103, actions: ['login'] }
// ]

複数の配列を組み合わせて新しい配列を生成

// 商品とサイズの組み合わせを生成(デカルト積)
const products = ['シャツ', 'パンツ'];
const sizes = ['S', 'M', 'L'];

const combinations = Array.from(
  { length: products.length * sizes.length },
  (_, i) => ({
    product: products[Math.floor(i / sizes.length)],
    size: sizes[i % sizes.length]
  })
);

console.log(combinations);
// [
//   { product: 'シャツ', size: 'S' },
//   { product: 'シャツ', size: 'M' },
//   { product: 'シャツ', size: 'L' },
//   { product: 'パンツ', size: 'S' },
//   ...
// ]

注意点とパフォーマンス

メモリ使用量に注意

Array.from()は新しい配列を生成するため、非常に大きなデータセット(数百万件など)を扱う場合はメモリに注意が必要です。

// ❌ パフォーマンス警告:大量データの場合
const largeArray = Array.from({ length: 10000000 }, (_, i) => i);
// メモリをたくさん消費し、ガベージコレクションが頻繁に発生

// ✅ 代替案:イテレータやジェネレータを使用
function* generateNumbers(max) {
  for (let i = 0; i < max; i++) {
    yield i;
  }
}

const numbers = generateNumbers(10000000);
for (const num of numbers) {
  // 必要な処理のみを実行
}

ブラウザ互換性

Array.from()はIE11では使用できません。プロジェクトがレガシーブラウザに対応する場合は、ポリフィルが必要です。

// ポリフィル例
if (!Array.from) {
  Array.from = (function() {
    const toStr = Object.prototype.toString;
    const isCallable = (fn) => typeof fn === 'function';
    
    return function from(arrayLike, mapFn, thisArg) {
      const C = this;
      const items = Object.prototype.slice.call(arrayLike);
      
      if (mapFn) {
        return items.map((item, i) => mapFn.call(thisArg, item, i));
      }
      return items;
    };
  }());
}

null/undefined のハンドリング

Array.from()の第一引数にnullやundefinedを渡すとエラーになります。

// ❌ エラーが発生
const result = Array.from(null); // TypeError

// ✅ 安全なハンドリング
const safeArrayFrom = (value, mapFn) => {
  if (!value) {
    return [];
  }
  return Array.from(value, mapFn);
};

console.log(safeArrayFrom(null)); // []
console.log(safeArrayFrom([1, 2, 3], x => x * 2)); // [2, 4, 6]

実務での応用例|複合処理

複数の技術を組み合わせた、実際のプロジェクトで見かける例を紹介します。

// 実務例:複数のAPI呼び出し結果を集約
class DataAggregator {
  async fetchAndProcess() {
    const endpoints = [
      '/api/users',
      '/api/products',
      '/api/orders'
    ];
    
    // 複数のAPIを並列呼び出し
    const responses = await Promise.all(
      Array.from(endpoints, endpoint => fetch(endpoint))
    );
    
    // レスポンスをJSONに変換
    const data = await Promise.all(
      Array.from(responses, response => response.json())
    );
    
    // データを統合して処理
    return Array.from(data, (item, index) => ({
      source: endpoints[index],
      count: item.length,
      items: item
    }));
  }
}

// 実務例:フォーム バリデーションエラーの集約
const validateForm = (formElement) => {
  const errors = Array.from(
    formElement.querySelectorAll('[aria-invalid=\"true\"]'),
    field => ({
      fieldName: field.name,
      errorMessage: field.getAttribute('data-error'),
      value: field.value
    })
  );
  
  return {
    isValid: errors.length === 0,
    errors: errors
  };
};

まとめ

Array.from()は一見シンプルなメソッドですが、実務では非常に活用場面が広いツールです。本記事で紹介した主なポイントは以下の通りです。

  • DOM操作:NodeListを配列に変換して、配列メソッドを使用可能にする
  • データ変換:第二引数のマッピング関数で、変換と配列化を一度に処理
  • 配列生成:{ length }プロパティで、特定の範囲や条件の配列を素早く生成
  • 型安全性:TypeScriptでの型定義により、バグを事前に防止
  • パフォーマンス:大量データではイテレータを検討し、メモリ効率を優先

Array.from()を適切に使いこなすことで、JavaScriptのコードがより簡潔で読みやすく、保守しやすくなります。今後のプロジェクトで、ぜひこれらのパターンを参考にしてください。

タイトルとURLをコピーしました