先日、今風のJavaScriptのコードについて、あるコードのサンプルを見せていただきました!そのコードを理解し、使いこなすことを目指して、今回はそのコードのポイントを一つずつ解説しながら、今風のJavaScriptの書き方や概念を学んでいきたいと思います!(^^ゞ
僕は全くの初心者で、初め見たときは全くわかりませんでしたが、わからないことを1つずつ調べていきながら徐々に解読し、今は理解できるようになりました!
長くなるかもですが、自分のような初心者や、復習をしたい方の役に立てると嬉しいです(^^♪
目次
- イテレータの理解
- メソッドチェーンとその魅力
- アロー関数の基本と使いどころ
Array.from
と_
の使い方toArray
メソッドとtake
メソッド- 今風のコードの具体例
- まとめと今後の学習ステップ
イテレータの理解
まず、イテレータとは、順番にデータを取り出せるオブジェクト です。JavaScriptでは、配列や文字列などの反復可能なオブジェクトに対して、イテレータを使うことでその要素を順番に取り出すことができます。
const lines = ['apple', 'banana', 'cherry'];
const iter = lines.values(); // イテレータを取得
上記のコードでは、lines.values()
によって イテレータ が作られます。イテレータは、順番に配列の各要素を取り出すための道具です。
イテレータには next()
メソッドがあり、これを呼び出すたびに配列の次の値が返されます。
const first = iter.next(); // { value: 'apple', done: false }
const second = iter.next(); // { value: 'banana', done: false }
-
value
は取り出した値 -
done
はそのイテレータが終わったかどうか
⭐イテレータのメリット
イテレータを使うメリットは、配列のインデックスを手動で管理する必要がなくなる ことです。これにより、for
ループを使ってインデックスを管理する煩わしさから解放されます。
例えば、次のようなコードでは、配列の要素をインデックスなしで順番に取り出せます。
const iter = lines.values();
console.log(iter.next().value); // "apple"
console.log(iter.next().value); // "banana"
メソッドチェーンとその魅力
次に、メソッドチェーンについて説明します。メソッドチェーンとは、複数のメソッドを 次々に呼び出す 書き方です。これによりコードがより簡潔で読みやすくなります。
例えば、配列を操作する際に、map()
や filter()
といったメソッドを連続して呼び出すことができます。
const nums = [1, 2, 3, 4, 5];
const result = nums
.map(num => num * 2) // 各要素を2倍にする
.filter(num => num > 5); // 5より大きい数だけを取り出す
console.log(result); // [6, 8, 10]
⭐メソッドチェーンの利点
メソッドチェーンを使うことで、中間変数を減らすことができ、コードが直感的で読みやすくなり、一貫性が保たれたまま操作を連続的に行うことができます。これにより、コードの可読性やメンテナンス性が向上します。
アロー関数の基本と使いどころ
次に、アロー関数について見ていきましょう。アロー関数は、従来の関数定義方法に比べて、よりシンプルで短い書き方ができます。特に、コールバック関数や簡単な計算処理でよく使われます。
const sum = (a, b) => a + b; // アロー関数の例
アロー関数の特徴
- 短く書ける:function キーワードを省略でき、簡潔に記述できる
-
this
を使わない:アロー関数はthis
をレキシカルにバインドするため、this
の挙動が直感的になる(←ちなみにここはよくわかってない(´;ω;`))
※余談 {}
の有無で意味が変わる
✅ {} あり(ブロックを作る)
const add = (a, b) => {
return a + b; // `return` が必要!
};
console.log(add(2, 3)); // 5
✅ ブロック {}
を使うと return
が必要(明示的に書く)
Array.from
と _
の使い方
JavaScriptでは、Array.from
メソッドを使って、指定されたオブジェクトから新しい配列を作成することができます。このメソッドには2つの引数を渡すことができ、
通常は「配列の各要素の値」と「インデックス番号」の2つが渡されます。
Array.from({ length: 3 }, (_, index) => index * 2);
⭐まず、Array.from
メソッドの構造を理解しましょう。以下のように使います:
Array.from(arrayLike, mapFunction)
-
arrayLike
: 配列に似たオブジェクト(例えばarguments
やNodeList
など)を渡すことができます。ここでは{ length: 3 }
というオブジェクトを渡しています。 -
mapFunction
: 配列の各要素をどのように変換するかを指定する関数です。
この mapFunction
は、2つの引数を受け取ります:
- 要素の値(この場合、
{ length: 3 }
なので要素の値はundefined
です) - 要素のインデックス番号(
0, 1, 2, ...
)
Array.from({ length: 3 }, (value, index) => index * 2);
// 出力: [0, 2, 4]
⭐_
とは?
_
は、いわゆる「使わない引数」を表すために使われる慣習です。特に、値を受け取る引数があるが、その値は実際には使わない場合に _
を使って、「この引数は意図的に無視している」と示すことができます。
このコードでは、Array.from
の mapFunction
の最初の引数 value
は、 { length: 3 }
のため、各要素の値はすべて undefined
になります。しかし、値は必要なく、インデックス番号(index
)だけを使いたい場合、最初の引数を _
として無視します。
Array.from({ length: 3 }, (_, index) => index * 2);
このコードは、インデックス番号に基づいて新しい配列を作成し、各インデックスに 2 を掛けています。結果として、以下の配列が生成されます:
[0, 2, 4]
なぜ _
を使うのか?
- コードの可読性を高めるため: _ を使うことで、「この引数は使わないことが意図されている」ということが明確になり、他の人(あるいは自分)がコードを読んだときに無駄な引数に気を取られなくて済みます。
- JavaScriptの規則ではない: _ はJavaScriptの言語仕様で強制されているわけではありませんが、一般的に使用されている慣習です。これは、関数の引数を意図的に無視したいときに、明確にその意図を示す方法として広く使われています。
- 無駄な変数の回避: 他の変数名を使うこともできますが、_ は「未使用の引数」を意味することが多いため、コードが簡潔でわかりやすくなります。
toArray
メソッドとtake
メソッド
⭐take
メソッドの簡単な例
take
メソッドは、イテレータや配列から最初のN個の要素を取得するものです。例えば、take
は通常 iter-tools
のようなライブラリに含まれていますが、JavaScript標準にはないので、通常の配列操作で代用します。ここでは take
を模倣した例を示します。
// 例: 最初の3つの要素を取得
const array = [1, 2, 3, 4, 5];
// take と同じ動作をする関数
const take = (arr, n) => arr.slice(0, n);
const firstThree = take(array, 3);
console.log(firstThree); // [1, 2, 3]
ここでは、配列から最初の n
個の要素を取り出すために、slice
メソッドを使っています。take
はこれと同様の動作をするイメージです。
⭐toArray
メソッドの簡単な例
toArray
メソッドは、イテレータを配列に変換するメソッドです。JavaScript標準の for...of
やスプレッド演算子 ...
を使うことで同じことができます。
// 例: イテレータを配列に変換
const lines = ['apple', 'banana', 'cherry'];
const iter = lines.values(); // イテレータを作成
// イテレータを配列に変換
const arrayFromIterator = Array.from(iter);
console.log(arrayFromIterator); // ['apple', 'banana', 'cherry']
ここでは Array.from
を使って、イテレータを配列に変換しています。これがまさに toArray
と同じ役割を果たします。
⭐take
と toArray
を使えるようにする実装
take
や toArray
は、ライブラリ(例えば、iter-tools
や lodash
)を使うと簡単に利用できます。
-
iter-tools
の導入
iter-tools
というライブラリを使えば、take
メソッドなどを簡単に利用できます。このライブラリは、イテレータを扱いやすくするための便利な関数を提供します。
npm install iter-tools
インストール後、以下のように take
や toArray
を使えます。
// iter-tools のインポート
const { take, toArray } = require('iter-tools');
-
lodash
を使う方法
lodash
は、配列やオブジェクトを簡単に操作するための便利なライブラリです。take
やtoArray
も含まれています。
npm install lodash
インストール後、以下のように使います。
// lodash のインポート
const _ = require('lodash');
今風のコードの具体例
先日書いた記事の@ttatsf(tatsuo fukuchi)さんからいただいたコメントのものです!
ここまで読んでくださったのなら、このコードが理解できるはず!できるといいな(´;ω;`)
const iter = lines.values(); // 配列のイテレータを取得
const n = Number(iter.next().value); // 1行目を取得し、数値に変換
const players = iter.take(n).toArray().sort(); // n人の名前を取得し、ソート
const damages = new Map(players.map(e => [e, 0])); // 各プレイヤーのダメージを0で初期化
const m = Number(iter.next().value); // ダメージ情報の行数を取得
iter.take(m)
.map(e => e.split(" "))
.map(([_, b]) => [_, Number(b)]) // 数値変換
.forEach(([k, v]) => damages.set(k, damages.get(k) + v)); // ダメージを加算
damages.forEach(v => console.log(v)); // ダメージを出力
⭐コードの簡単な解説
1.イテレータの活用
lines.values()
によって、lines
配列の要素を順番に取得できるイテレータを作成しています。この方法を使うことで、インデックスを使わずに要素を取り出すことができます。
2.Array.from()
と { length: n }
の使い方
Array.from({ length: n })
を使うことで、n 個の要素を持つ配列を作成します。その中で iter.next().value
を使い、イテレータから順番に値を取り出して players
配列を作成します。
3.map
と forEach
の使い方
map
メソッドを使って、ダメージ情報を分割したり、数値に変換したりしています。その後、forEach
でダメージの累積を行い、最終的に結果を出力します。
⭐take
や toArray
を使わずに、同じ動作を実現するコードに変換する方法
ライブラリを使わない場合でも、標準の JavaScript の機能を使って同じことを実現できます
const players = iter.take(n).toArray().sort();
と iter.take(m)
を書き換えます。
// 最初の n 人の名前を取得
const players = Array.from({ length: n }, () => iter.next().value);
players.sort(); // プレイヤーをソート
// ダメージ情報を m 回分取得し、処理する
Array.from({ length: m }, () => iter.next().value)
まとめと今後の学習ステップ
このコードのように、イテレータ、メソッドチェーン、アロー関数 といったテクニックを使うことで、より簡潔で効率的なコードを書くことができます。これらの概念を理解し使いこなすことは、より高度なJavaScriptの技術を学ぶためのステップとなると思いました。
次のステップとしては、非同期処理やパフォーマンスの最適化、より複雑なデータ構造の取り扱いなどに挑戦してみたいです。
しかし、まずは、今回学習したイテレータやメソッドチェーンなどを他の問題の場合でも応用できるように練習し、習得することですね。
コメントをきっかけに学びを深められるのをとても嬉しく、ありがたく思っています。
改めて、@ttatsf(tatsuo fukuchi)さんコメントありがとうございました(^^ゞ