こんにちわ!今日もTypescriptを書いてるSuです。今回は、Typescriptで、配列のmap関数を使って、中身を入れ替える書き方を紹介したいと思います。
特に難しいことはないですが、より理解しやすく、短いコードが書けるようになると、保守性が上がりますので、ぜひこういうノウハウは覚えていきたいですね。
map関数とは?
map関数は、Arrayのprototypeに定義されている、配列の中身をループしながら、新しい配列を作る関数です。
至る所で利用され、javascript, Typescriptを書いている人なら必須で覚えなければいけない関数の1つだと思います。
Mozillaのドキュメントはこちら。Mozillaのドキュメントはホントお世話になってます。
map関数でデータを入れ替える方法
こんな感じのUserデータがあります。name, hobby, secretというキーがあるオブジェクトの配列です。
// base UserType
type UserType = {
name: string;
hobby: string;
secret: string;
};
// base user list
const userList: UserType[] = [
{ name: "Sato", hobby: "Game", secret: "I love Ito-san" },
{ name: "Ito", hobby: "Movie", secret: "I love Takahashi-san" },
{ name: "Goto", hobby: "Sport", secret: "I love Abe-san" },
];
ここからsecretを除くロジックをmap関数を使って書いていきましょう。Typescriptの場合は、Omitを使って、既存の型定義から一部のキーを除いた型を作ることができます。以下のような感じで書くとUserInfoTypeから、secretを除いた型 PublicUserInfoTypeを作ることができます。
// base UserType
type UserType = {
name: string;
hobby: string;
secret: string;
};
// omitted UserType
type PublicUserType = Omit<UserType, "secret">;
for文を使ってsecretを除く
map関数の前に、良くあるfor文を使って、secretを除くとこのような形になります。変数を定義して初期化しないといけないので、ちょっと煩雑ですね。また、letを使うと、変数をあとから書き換えることができてしまうので、なるべくなら使いたくないですね。
// for of
let publicUser1: PublicUserType[] = [];
for (const user of userList) {
const { name, hobby } = user;
publicUser1.push({ name, hobby });
}
map関数を使ってsecretを除く その1
では、map関数を使って、secretを除いてみましょう。こんな感じになりますね。各行のname, hobbyだけ返すようにすることで、secretを除いた新しい配列を作ることができます。
// map 1
const publicUser2: PublicUserType[] = userList.map((user) => ({
name: user.name,
hobby: user.hobby,
}));
少し解説すると、map関数は引数に関数を受け取ることができます。こんな感じです。Aにはarrayの中のオブジェクトが入ります。つまり、A.hogeとかA.hugaとかでデータにアクセスできるようになります。Aの名前は任意ですが、内容が分かるような名前の方が、他の方が理解しやすいので良いですね。(レビューとかでも良く、「意味が分かりづらいのでこんな変数名にしませんか?」という提案を受けたりします)
array.map(function(A){
A.hoge;
B.huga;
})
function(A)の部分はアロー関数式に置き換えられるので、以下の様な形に変更しています。functionと毎回書くのは面倒ですからね。
// function type
array.map(function(A){
return {
A.hoge,
B.huga,
};
})
// arrow function type
array.map((A) => {
return {
A.hoge,
B.huga,
};
});
また、関数の戻り値に関しては、通常は関数内で、以下のようにreturn文を書くのですが、戻り値を1行で書ける場合は、「()」で囲むことで、return文は省略できます。
※これはオブジェクトを返す場合限定なので、通常は「()」も不要です。
// return type
array.map((A) => {
return {
A.hoge,
B.huga,
};
});
// no return type
array.map((A) => ({
A.hoge,
B.huga,
})
);
結果この形になります。mapの戻り値(publicUser2)は新しい配列であるため、既存のuserListの変更に影響は受けません。これは影響の少ない良い実装と言えます。
// map 1
const publicUser2: PublicUserType[] = userList.map((user) => ({
name: user.name,
hobby: user.hobby,
}));
map関数を使ってsecretを除く その2
先ほどのmap関数、user.name, user.hobbyという部分がちょっと煩雑ですね。
// map 1
const publicUser2: PublicUserType[] = userList.map((user) => ({
name: user.name,
hobby: user.hobby,
}));
「オブジェクトの展開代入」と「オブジェクトの省略記法」を使って簡略化してみましょう。このような感じになります。
// map 2
userList.map(({ name, hobby }) => ({
name,
hobby,
}));
まず、「オブジェクトの展開代入」について見ていきましょう。オブジェクトから必要なデータだけ取り出す方法が簡単に書けます。
展開代入を使わない場合は、こんな感じで1つずつ入れていく方法になります。
const name = user.name;
const hobby = user.hobby;
オブジェクトの展開代入を使う場合は、こんな感じでまとめて書けます。必要な値だけ取り出しつつ、変数も定義するので、非常にシンプルに書けますね。
const { name, hobby } = user;
先ほどの関数に戻ると、以下の様な変更を行ったことになります。展開した結果を書くことで、必要なデータのみ記述することができるようになりました。
userList.map((user) => ({
name: user.name,
hobby: user.hobby,
}));
// ↓ (user)のところを、({name, hobby})に置き換えた
userList.map(({ name, hobby }) => ({
name: name,
hobby: hobby,
}));
さらに、「オブジェクトの省略記法」を使いましょう。以下の部分は、name: nameのように同じ言葉が並んでいて煩雑そうですね。
{
name: name,
hobby: hobby,
}
「オブジェクトの省略記法」は、「キー名と値に設定する変数名が同一の場合は値を省略できる」という記法です。以下こんな感じの変更になります。非常にシンプルに書けますね。
userList.map(({ name, hobby }) => ({
name: name,
hobby: hobby,
}));
// ↓ 「name: name」のところを、「name」に置き換えた
userList.map(({ name, hobby }) => ({
name,
hobby,
}));
最終形がこちらです。最初のmap その1よりだいぶ短くシンプルに書けました!
// map 2
const publicUser2: PublicUserType[] = userList.map(({ name, hobby }) => ({
name,
hobby,
}));
最後に
ここまで読んでくださり、ありがとうございました。Typescriptは非常に強力な言語です。ただ、型定義、省略記法などたくさんの仕様を覚えていないとなかなか効果の高いコードを書くことは難しいです。ですので、いろいろな書き方を覚えて、もっと楽しいコーディングライフを送りましょう!