はじめに
社内の輪読会で『良いコード/悪いコードで学ぶ設計入門』の第9章「コレクションーネストを解消する構造化技法ー」を担当しました。本記事では、その際に作成した資料を公開します。
(本書ではJavaのコードが掲載されていますが、本記事ではTypeScriptを用いて解説します)
配列でスマートな処理の書き方を紹介
なぜ?:ループの中に条件分岐がネストしていくと、コードが複雑化し、可読性が低下するため。
ゴール:シンプルで理解しやすいコードを書くための技法を学ぶ。
標準メソッドが無いか確認する
コード例
配列にオレンジがあるかどうか確認する処理
❌️for文
const items = [{ name: 'apple' }, { name: 'orange' }, { name: 'grape' }];
let hasOrange = false;
for (let i = 0; i < items.length; i++) {
if (items[i].name === 'orange') {
hasOrange = true;
}
}
✅️someメソッド
1つでも返り値にtrueがあればtrueを返す
const items = [{ name: 'apple' }, { name: 'orange' }, { name: 'grape' }];
const hasOrange = items.some((item) => item.name === 'orange');
ループ中の条件分岐のネストをしない
早期continueでネストを減らす
コード例
年齢30歳以上、身長160cm以上、体重60kg以上の人物を抽出する処理
❌️for文の例
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
];
let targetUsers = [];
for (let i = 0; i < users.length; i++) {
if (users[i].age >= 30) {
if (users[i].height >= 160) {
if (users[i].weight >= 60) {
targetUsers.push(users[i]);
}
}
}
}
✅️早期continue
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
];
let targetUsers = [];
for (let i = 0; i < users.length; i++) {
if (users[i].age < 30) {
continue;
}
if (users[i].height < 160) {
continue;
}
if (users[i].weight < 60) {
continue;
}
targetUsers.push(users[i])
}
😇filterメソッド
返り値がtrueの要素を返す
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
];
const targetUsers = users.filter((user) => {
return user.age >= 30 && user.height >= 160 && user.weight >= 60;
});
breakでもネスト解消できる
全員がBMIで普通体重かどうか(18.50〜24.99以下)
コード例
❌️ネストしている
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
];
let isAllHealthy = false;
for (let i = 0; i < users.length; i++) {
const bmi = users[i].weight * Math.pow(users[i].height, 2);
if (bmi > 18.5) {
if (bmi < 25) {
isAllHealthy = true;
}
}
}
✅️breakを使用
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
];
let isAllHealthy = true;
for (let i = 0; i < users.length; i++) {
const bmi = users[i].weight * Math.pow(users[i].height, 2);
if (bmi <= 18.5) {
isAllHealthy = false;
break;
}
if (bmi > 25) {
isAllHealthy = false;
}
}
😇everyメソッドを使用
すべての返り値がtrueならtrueを返す
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
];
const isAllHealthy = users.every((user) => {
const bmi = user.weight * Math.pow(user.height, 2);
return bmi <= 18.5 && bmi > 25;
});
処理のカプセル化
配列操作ロジックと、その操作結果を使うロジックを分離する。
コード例
❌️体重の合計を計算する処理が重複している
type User = {
name: string;
weight: number;
};
function processReport(users: User[]) {
let totalWeight = 0;
for (let i = 0; i < users.length; i++) {
totalWeight += users[i].weight;
}
console.log(`合計体重は ${totalWeight} kg です。`);
// ... その他のレポート処理
}
function checkHealthStatus(users: User[]) {
let totalWeight = 0;
for (let i = 0; i < users.length; i++) {
totalWeight += users[i].weight;
}
if (totalWeight > 200) {
console.log('グループの合計体重が200kgを超えています。');
}
}
✅️外部関数を呼び出すことで、体重の合計の計算処理が重複しない
type User = {
name: string;
weight: number;
};
// 体重の合計を計算する処理を専用の関数としてカプセル化
function calculateTotalWeight(users: User[]) {
// reduceで合計を計算
return users.reduce((total, user) => total + user.weight, 0);
}
function processReport(users: User[]) {
const totalWeight = calculateTotalWeight(users);
console.log(`合計体重は ${totalWeight} kg です。`);
// ... その他のレポート処理
}
function checkHealthStatus(users: User[]) {
const totalWeight = calculateTotalWeight(users);
if (totalWeight > 200) {
console.log('グループの合計体重が200kgを超えています。');
}
}
まとめ
- 配列の標準メソッドを知ることが大事
- 車輪の再発明はしない(学習のためならOK)
- 処理は(なるべく)共通化する
クイズ
<メソッド>
に何のメソッドが入るか考えよう❗️
すべて出来れば君も配列マスターだ❗️
第1問
すべてのユーザーの体重を-3する
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
{ name: 'satoshi', age: 25, height: 180, weight: 60 },
];
const healthyUsers = users.<メソッド>((user) => {
return {
...user,
weight: user.weight - 3,
};
});
解答
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
{ name: 'satoshi', age: 25, height: 180, weight: 60 },
];
const healthyUsers = users.map((user) => {
return {
...user,
weight: user.weight - 3,
};
});
第2問
配列の1番目と2番目のユーザーを抽出する(元の配列は変更せずに)
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
{ name: 'satoshi', age: 25, height: 180, weight: 60 },
];
const targetUsers = users.<メソッド>(1, 3);
解答
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
{ name: 'satoshi', age: 25, height: 180, weight: 60 },
];
const targetUsers = users.slice(1, 3);
第3問
配列の最後から見て最初に年齢が25歳のユーザーを取得する
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
{ name: 'satoshi', age: 25, height: 180, weight: 60 },
];
const targetUser = users.<メソッド>(user => user.age === 25);
解答
const users = [
{ name: 'takeshi', age: 30, height: 172, weight: 70 },
{ name: 'hanako', age: 27, height: 161, weight: 43 },
{ name: 'kenta', age: 25, height: 188, weight: 46 },
{ name: 'satoshi', age: 25, height: 180, weight: 60 },
];
const targetUser = users.findLast(user => user.age === 25);
参考記事
最後に
GoQSystemでは一緒に働いてくれる仲間を募集中です!
ご興味がある方は以下リンクよりご確認ください。