JavaScriptには、いくつでも要素を扱えるようにする組み込みの...演算子があります。使われる場面によって、残余演算子 または スプレッド演算子 と呼ばれます。
代入の左辺に...が現れるとき、この3つのドットはrest演算子と呼ばれます。3つのドットと変数名を合わせたものは、残余要素と呼ばれます。残余要素は0個以上の値を集めて、1つの配列にまとめます。
const [a, b, ...everythingElse] = [0, 1, 1, 2, 3, 5, 8];
a;
// => 0
b;
// => 1
everythingElse;
// => [1, 2, 3, 5, 8]
JavaScriptでは、他の言語とは違って、rest要素の末尾にカンマを置くことはできません。分割代入では、_必ず_最後の要素でなければなりません。次の例はSyntaxErrorをスローします。
const [...items, last] = [2, 4, 8, 16]
配列と同じように、rest演算子を使って1つ以上のオブジェクトのプロパティを集め、1つのオブジェクトにまとめることもできます。
const { street, ...address } = {
street: 'Platz der Republik 1',
postalCode: '11011',
city: 'Berlin',
};
street;
// => 'Platz der Republik 1'
address;
// => {postalCode: '11011', city: 'Berlin'}
関数定義で、最後の引数のとなりに...が現れると、その仮引数は 残余引数 と呼ばれます。これを使うと、関数はいくつでも引数を配列として受け取れます。
function concat(...strings) {
return strings.join(' ');
}
concat('one');
// => 'one'
concat('one', 'two', 'three');
// => 'one two three'
代入の右辺に...が現れると、それはspread演算子と呼ばれます。配列を要素のリストに展開します。残余要素と違って、配列リテラル式のどこにでも書くことができ、複数使うこともできます。
const oneToFive = [1, 2, 3, 4, 5];
const oneToTen = [...oneToFive, 6, 7, 8, 9, 10];
oneToTen;
// => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const woow = ['A', ...oneToFive, 'B', 'C', 'D', 'E', ...oneToFive, 42];
woow;
// => ["A", 1, 2, 3, 4, 5, "B", "C", "D", "E", 1, 2, 3, 4, 5, 42]
配列と同じように、spread演算子を使って、あるオブジェクトのプロパティを別のオブジェクトにコピーすることもできます。
let address = {
postalCode: '11011',
city: 'Berlin',
};
address = { ...address, country: 'Germany' };
// => {
// postalCode: '11011',
// city: 'Berlin',
// country: 'Germany',
// }
友人のLinusは、都市の間で貨物列車を運転する機関士です。 列車の扱いは素晴らしく得意ですが、物流やコンピューターの扱いは得意ではありません。 そこで、列車の情報を整理し、ルートデータの間違いを直すために、あなたのプログラミングの助けを借りたいと考えています。
練習として、以下の各タスクを解くときは、残余引数またはスプレッド演算子を使ってみましょう。
友人はこれまで各車両の識別子(ID)を記録してきましたが、システムが一度に何両の車両を処理することになるのか、いつも確信が持てません。
このデータを1つのarrayにまとめておけば、物流プログラムの残りの部分はずっと楽になります。
任意の数の車両IDを受け取る関数getListOfWagonsを実装しましょう。この車両IDは、それぞれの車両のIDです。
それぞれのIDは正の整数です。
関数は、受け取ったIDを1つのarrayとして返します。
getListOfWagons(1, 7, 12, 3, 14, 8, 5);
// => [1, 7, 12, 3, 14, 8, 5]
ここまでで、データと、それが物流プログラムでどのように使われるかが、少しずつわかってきたのではないでしょうか。 IDシステムでは、機関車には必ず1というIDが割り当てられ、列車の残りの車両には1より大きいIDがランダムに割り当てられます。
友人は列車に新しい車両を2両つなげることになりましたが、システムを更新するのを忘れてしまいました! そのため、列車のarrayの最初の2両を末尾に移動しないと、すべてが順番どおりになりません。
Linusは、間違いを直してくれたら、とても感謝するでしょう。
各車両のIDの配列を受け取る関数fixListOfWagonsを実装しましょう。
この関数は、機関車が先頭に来るように、最初の2つの要素をarrayの末尾に移動したarrayをreturnします。
eachWagonsID = [2, 5, 1, 7, 4, 12, 6, 3, 13];
fixListOfWagons(eachWagonsID);
// => [1, 7, 4, 12, 6, 3, 13, 2, 5]
おっと。どうやらいくつかの車両が行方不明になってしまったようです。
幸いなことに、友人は、失われた車両IDが入っていそうな別のarrayを見つけたので、それを車両IDのメインのarrayに追加してほしいと考えています。
覚えているのは、失われた値は指定された機関車のすぐ後ろに置く必要があるということだけです。
この新しい情報をもとに、各車両のIDを持つ2つの配列を引数に取るcorrectListOfWagonsという関数を書きましょう。
2つ目のarrayの車両IDは、機関車(ID 1)のすぐ後ろになるように、1つ目のarrayに追加します。
eachWagonsID = [1, 5, 20, 7, 4, 8];
missingWagons = [3, 17, 6, 15];
correctListOfWagons(eachWagonsID, missingWagons);
// => [1, 3, 17, 6, 15, 5, 20, 7, 4, 8]
車両のデータがすべて正しくなったので、友人はシステムのルート情報を更新してほしいと考えています。
最初のルート情報はobjectとして作られており、友人は、提供された追加情報でそれを更新してほしいと思っています。
ルートごとに必要な情報は少しずつ異なるため、友人は汎用的な解決策を強く望んでいます。
2つのobjectを受け取る関数extendRouteInformationを実装しましょう。
1つ目のobjectには、列車のルートがどの都市の間を移動するかが入っています。
2つ目のobjectには、列車の速度や長さなど、その他のルートの詳細が入っています。
関数は、すべてのルート情報をまとめた1つのobjectsを返します。
変数moreRouteInformationには、さまざまなプロパティが入る可能性があります。
route = { from: 'Berlin', to: 'Hamburg' };
moreRouteInformation = { length: '100', speed: '50' };
extendRouteInformation(route, moreRouteInformation);
// => {from: "Berlin", to: "Hamburg", length: "100", speed: "50"}
友人は、ルート情報に到着時刻は必要ないと気づきました。 そこで、到着時刻をルート情報から分離してほしいと考えています。
ルート情報を持つオブジェクトを受け取る関数separateTimeOfArrivalを実装しましょう。
関数は、最初の要素が到着時刻、2つ目の要素が到着時刻を除いたルート情報を持つオブジェクトである配列を返します。
routeInformation = {
from: 'Berlin',
to: 'Hamburg',
length: '100',
timeOfArrival: '10:10',
};
separateTimeOfArrival(routeInformation);
// => ["10:10", {from: "Berlin", to: "Hamburg", length: "100"}]
Exercismに登録すれば、37個のコンセプト159個の演習、そして本物の人間によるメンタリングとともに、JavaScriptを学んでマスターできます。すべて無料です。