JavaScriptでPythonのenumerate関数を実行する7つの方法

JavaScriptでPythonのenumerate関数を実行する7つの方法JS
この記事は約15分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●JavaScriptでenumerateを実現する7つの方法

JavaScriptを学び始めたばかりの頃、Pythonで便利だったenumerate関数と同等の機能がJavaScriptにないことに戸惑った経験はないでしょうか。

enumerate関数は、イテラブルなオブジェクトの要素とそのインデックスを同時に取得できる優れたツールです。

JavaScriptでは組み込みのenumerate関数はありませんが、同じような機能を実現する方法はいくつもあります。

JavaScriptでenumerateと同等の機能を実装する方法として、for…of文、Array.prototype.entries、Object.entries、Array.prototype.map、ジェネレーター関数、JSZipライブラリ、独自のenumerate関数などがあります。

それぞれの方法には特徴があり、状況に応じて使い分けることが大切です。

○サンプルコード1:for…of文でenumerate

for…of文は、ES2015(ES6)で導入された構文で、イテラブルなオブジェクトの要素を順番に取得することができます。

for…of文とArray.prototype.entriesメソッドを組み合わせることで、Pythonのenumerate関数と同じような機能を実現できます。

const fruits = ['apple', 'banana', 'orange'];

for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}

このコードでは、fruitsの各要素とそのインデックスを同時に取得し、テンプレートリテラルを使ってコンソールに出力しています。

for…of文は分かりやすく、ES2015以降のモダンな環境で使える方法です。

○サンプルコード2:Array.prototype.entriesでenumerate

Array.prototype.entriesメソッドは、配列のインデックスと要素のペアを含む新しいArray Iteratorオブジェクトを返します。

このメソッドを使えば、for…of文を使わずともenumerateと同等の機能を実装できます。

const fruits = ['apple', 'banana', 'orange'];

const enumerated = Array.from(fruits.entries());
console.log(enumerated);

Array.fromメソッドを使って、Array Iteratorを配列に変換しています。

enumeratedには、インデックスと要素のペアが配列として格納されます。

Array.prototype.entriesメソッドは、for…of文と組み合わせなくても単独で使えるのが特徴です。

○サンプルコード3:Object.entriesでenumerate

Object.entriesメソッドは、オブジェクトの列挙可能なプロパティの[key, value]のペアの配列を返します。

このメソッドを使えば、オブジェクトのプロパティとその値を同時に取得できます。

const person = { name: 'Alice', age: 20, city: 'Tokyo' };

for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

Object.entriesメソッドとfor…of文を組み合わせることで、オブジェクトのプロパティとその値を簡単に列挙できます。

オブジェクトに対してenumerateと同等の機能を使いたい場合に便利な方法です。

○サンプルコード4:Array.prototype.mapでenumerate

Array.prototype.mapメソッドを使って、配列の要素とインデックスを組み合わせた新しい配列を作ることもできます。

mapメソッドは、配列の各要素に対して指定したコールバック関数を実行し、その結果から新しい配列を生成します。

const fruits = ['apple', 'banana', 'orange'];

const enumerated = fruits.map((fruit, index) => [index, fruit]);
console.log(enumerated);

mapメソッドのコールバック関数では、第1引数に要素、第2引数にインデックスが渡されます。

これを利用して、インデックスと要素のペアを配列として返すことで、enumerateと同等の機能を実現しています。

○サンプルコード5:ジェネレーター関数でenumerate

ジェネレーター関数を使えば、カスタムのイテレーターを作ることができます。

ジェネレーター関数は、function*キーワードを使って定義し、yieldキーワードを使って値を返します。

ジェネレーター関数を使ってenumerate関数を実装してみましょう。

function* enumerate(iterable) {
  let index = 0;
  for (const item of iterable) {
    yield [index, item];
    index++;
  }
}

const fruits = ['apple', 'banana', 'orange'];

for (const [index, fruit] of enumerate(fruits)) {
  console.log(`${index}: ${fruit}`);
}

enumerate関数は、引数で受け取ったiterableオブジェクトの要素とインデックスのペアをyieldキーワードで返します。

この関数を使えば、任意のイテラブルなオブジェクトに対してenumerate機能を使うことができます。

○サンプルコード6:JSZipを使ったenumerate

JSZipは、JavaScriptでZIPファイルを生成したり読み込んだりするためのライブラリです。

JSZipのforEachメソッドを使えば、ZIPファイル内のエントリーとそのインデックスを同時に取得できます。

import JSZip from 'jszip';

const zip = new JSZip();
zip.file('hello.txt', 'Hello World');
zip.file('README.md', '# README');

zip.forEach((relativePath, zipEntry) => {
  console.log(`${zipEntry.name}: ${relativePath}`);
});

JSZipのforEachメソッドは、ZIPファイル内の各エントリーに対してコールバック関数を実行します。

コールバック関数の第1引数にはエントリーの相対パス、第2引数にはZipEntryオブジェクトが渡されます。

これを利用して、ZIP内のファイル名とパスを列挙しています。

○サンプルコード7:独自のenumerate関数

これまで紹介した方法とは別に、独自のenumerate関数を実装することもできます。

シンプルなenumerate関数を実装するなら、次のようなコードになります。

function enumerate(iterable) {
  return Array.from(iterable).map((item, index) => [index, item]);
}

const fruits = ['apple', 'banana', 'orange'];
console.log(enumerate(fruits));

独自のenumerate関数では、Array.fromメソッドを使ってiterableを配列に変換し、mapメソッドで各要素とそのインデックスのペアを作っています。

この関数を使えば、任意のイテラブルなオブジェクトに対してenumerate機能を使うことができます。

●JavaScriptでenumerateを使う際の注意点

JavaScriptでenumerateと同等の機能を実装する方法は様々ありますが、使う際には注意点があります。

Pythonのenumerate関数に慣れている人は、JavaScriptでの実装の違いに戸惑うかもしれません。

ここでは、JavaScriptでenumerateを使う際の注意点を見ていきましょう。

○forEachとの違い

JavaScriptの配列にはforEachメソッドがあり、これを使って配列の要素を順番に処理することができます。

しかし、forEachメソッドではインデックスを直接取得することができません。

一方、enumerateを使えば、要素とインデックスを同時に取得できるので、より柔軟な処理が可能です。

forEachメソッドを使った場合

const fruits = ['apple', 'banana', 'orange'];

fruits.forEach((fruit) => {
  console.log(fruit);
});

enumerateを使った場合

const fruits = ['apple', 'banana', 'orange'];

for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}

enumerateを使えば、インデックスを使った条件分岐や、要素とインデックスを組み合わせた処理が簡単に書けます。

状況に応じて、forEachとenumerateを使い分けることが大切ですね。

○パフォーマンスへの影響

JavaScriptでenumerateと同等の機能を実装する際は、パフォーマンスへの影響を考慮する必要があります。

特に、大きな配列やオブジェクトを扱う場合は、パフォーマンスの差が顕著に現れることがあります。

例えば、Array.prototype.entriesメソッドを使ってenumerateを実装する場合、新しい配列がメモリ上に作成されます。

これは、配列のサイズが大きくなるほどメモリの消費量が増えることを意味します。

const largeArray = Array(1000000).fill(0);

// Array.prototype.entriesを使った場合
const enumerated = Array.from(largeArray.entries());

一方、ジェネレーター関数を使ってenumerateを実装すれば、新しい配列を作成せずに済むので、メモリの消費量を抑えることができます。

function* enumerate(iterable) {
  let index = 0;
  for (const item of iterable) {
    yield [index, item];
    index++;
  }
}

// ジェネレーター関数を使った場合
for (const [index, item] of enumerate(largeArray)) {
  // 処理
}

パフォーマンスへの影響を最小限に抑えるには、状況に応じて適切な実装方法を選ぶことが重要です。

大きなデータを扱う場合は、ジェネレーター関数を使うのが良いでしょう。

○非推奨のfor…in文について

JavaScriptにはfor…in文という構文があり、これを使ってオブジェクトのプロパティを列挙することができます。

しかし、for…in文はパフォーマンスの問題があることや、プロトタイプチェーン上のプロパティも列挙してしまうことから、使うことが推奨されていません。

const person = { name: 'Alice', age: 20 };

// 非推奨のfor...in文
for (const key in person) {
  console.log(`${key}: ${person[key]}`);
}

for…in文の代わりに、Object.entriesメソッドとfor…of文を使うことが推奨されています。

これにより、パフォーマンスの問題を回避し、意図しないプロパティが列挙されるのを防ぐことができます。

const person = { name: 'Alice', age: 20 };

// 推奨されるObject.entriesとfor...of文
for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

for…in文を使うと、コードの可読性が下がり、バグを引き起こす可能性があります。

JavaScriptでenumerateを実装する際は、for…in文は避けるようにしましょう。

●enumerateの応用例

JavaScriptでenumerateと同等の機能を実装する方法を学んだら、それを応用してみましょう。

enumerateは配列やオブジェクトの要素を列挙するだけでなく、様々な場面で活用できます。

ここでは、enumerateの応用例をいくつか紹介します。

○サンプルコード8:オブジェクトのキーと値を同時に取得

オブジェクトのキーと値を同時に取得したい場合は、Object.entriesメソッドとfor…of文を使うと便利です。

Object.entriesメソッドは、オブジェクトの列挙可能なプロパティの[key, value]のペアの配列を返します。

const person = { name: 'Alice', age: 20, city: 'Tokyo' };

for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

このコードでは、personオブジェクトのキーと値のペアを順番に取得し、テンプレートリテラルを使ってコンソールに出力しています。

オブジェクトのプロパティを列挙する際に、キーと値を同時に扱いたい場合に役立つテクニックです。

○サンプルコード9:複数の配列を同時にループ

複数の配列を同時にループしたい場合は、Array.prototype.entriesメソッドとfor…of文を組み合わせると実現できます。

entriesメソッドで各配列のインデックスと要素のペアを取得し、それらを同時にループ処理します。

const fruits = ['apple', 'banana', 'orange'];
const prices = [100, 150, 200];

for (const [i, fruit] of fruits.entries()) {
  console.log(`${fruit}: ${prices[i]}円`);
}

このコードでは、fruitsとpricesという2つの配列を同時にループ処理しています。

fruitsの要素とそのインデックスを取得し、pricesの同じインデックスの要素を参照することで、フルーツの名前と価格を組み合わせて出力しています。

複数の配列を連動させて処理したい場合に便利な方法ですね。

○サンプルコード10:インデックス付きのマップを作成

enumerateを使えば、配列の要素とそのインデックスを組み合わせたマップ(オブジェクト)を簡単に作ることができます。

Array.prototype.entriesメソッドとObject.fromEntriesメソッドを組み合わせると、インデックス付きのマップが作れます。

const fruits = ['apple', 'banana', 'orange'];

const fruitMap = Object.fromEntries(fruits.entries());
console.log(fruitMap);

このコードでは、fruitsの要素とそのインデックスのペアから、インデックスをキー、要素を値とするマップ(オブジェクト)を作成しています。

Object.fromEntriesメソッドは、[key, value]のペアの配列からオブジェクトを作成します。

インデックスを使ってすばやく要素にアクセスしたい場合に役立つテクニックです。

○サンプルコード11:ジェネレーターを使った無限シーケンス

ジェネレーター関数を使えば、無限シーケンスを作ることができます。

ジェネレーター関数とenumerateを組み合わせると、インデックス付きの無限シーケンスが作れます。

function* infiniteSequence() {
  let index = 0;
  while (true) {
    yield index;
    index++;
  }
}

for (const [index, value] of enumerate(infiniteSequence())) {
  if (index >= 10) break;
  console.log(`${index}: ${value}`);
}

このコードでは、infiniteSequence関数が0から始まる無限の整数シーケンスを生成します。

enumerate関数とfor…of文を使って、シーケンスの要素とそのインデックスを取得し、インデックスが10以上になったら処理を終了しています。

無限シーケンスを扱う際に、enumerateを使ってインデックスを管理すると便利ですね。

まとめ

JavaScriptにはPythonのような組み込みのenumerate関数はありませんが、同等の機能を実装する方法はたくさんあります。

for…of文、Array.prototype.entries、Object.entries、Array.prototype.map、ジェネレーター関数、JSZipライブラリ、独自のenumerate関数など、様々なアプローチを解説してきました。

それぞれの方法には特徴があり、状況に応じて使い分けることが大切ですね。

enumerateを使う際は、forEachとの違いやパフォーマンスへの影響にも注意が必要です。

特に、大きなデータを扱う場合は、メモリ消費量の少ないジェネレーター関数を選ぶなど、適切な実装方法を選ぶことが重要です。

また、非推奨のfor…in文は避けるようにしましょう。

enumerateは配列やオブジェクトの要素を列挙するだけでなく、様々な場面で活用できます。

複数の配列を同時にループしたり、インデックス付きのマップを作ったり、無限シーケンスを扱ったりと、アイデア次第で多彩な応用が可能です。

JavaScriptでenumerateを使いこなすことで、コーディングの幅が広がるはずです。

ぜひ、実際のプロジェクトでも応用してみてください。

初めは戸惑うかもしれませんが、使っているうちにその便利さが実感できるはずです。

この記事が、皆さんのJavaScriptの学習の一助となれば幸いです。