先ほど例題をさらに詳しい解説付きで解答を作ったの確認してみましょう。
買い物 基本の合計 calcTotal
コード
function calcTotal(price, count = 1) {
return price * count;
}
JavaScript解説
- 目的: 単価 price と個数 count から合計を返す。count を省略しても 1個扱いにする。
- デフォルト引数: count = 1 により count が渡されなかったときに自動で 1 になる。これで undefined による間違いを防げる。
- 注意点: price に文字列が入ると数値計算できないことがある。実際のアプリでは入力チェックが必要。
テストと期待値
console.log(calcTotal(300)); // 300
console.log(calcTotal(300, 4)); // 1200
JavaScript拡張アイデア
- price に小数や通貨フォーマットを扱う。toFixed を使って表示を整える。
買い物 割引適用 calcDiscountedTotal
コード
function calcDiscountedTotal(price, count = 1, discount = 0) {
const subtotal = price * count;
const discounted = subtotal * (1 - discount);
return Math.round(discounted);
}
JavaScript解説
- 目的: 割引率 discount を使って割引後の合計を計算する。discount は 0 から 1 の値を想定(例 0.1 は 10% 引き)。
- 流れ: subtotal を計算し、それに (1 – discount) を掛ける。Math.round を使って四捨五入して整数にしている。
- 入力チェック: discount が負や 1 を超える場合は不正なのでガードを入れる実装も考えられる。
テスト
console.log(calcDiscountedTotal(1000, 2, 0.1)); // 1800
console.log(calcDiscountedTotal(1000)); // 1000
JavaScript拡張アイデア
- 割引の上限や下限をバリデーションする。割引額を別に返して明示する。
買い物 複数商品の合計と送料 sumCart
コード
function sumCart(items) {
// items は [{ price: 100, count: 2 }, ...]
let subtotal = 0;
for (let i = 0; i < items.length; i++) {
const item = items[i];
const count = item.count === undefined ? 1 : item.count;
subtotal += item.price * count;
}
const shipping = subtotal < 5000 ? 500 : 0;
return { subtotal, shipping, total: subtotal + shipping };
}
JavaScript解説
- 目的: 商品配列 items を受け取り小計、送料、合計を返す。
- 設計のポイント:
- items の各要素が count を持たない場合があるため count をデフォルトで 1 にしている。
- 送料ルールは subtotal < 5000 のとき 500、それ以上は無料。
- 戻り値をオブジェクトにして複数の値を返すことで呼び出し側で使いやすくしている。
- 注意点: items の各 price が数値であることを前提にしている。実運用では型チェックが必要。
テスト
console.log(sumCart([{price:1200, count:2}, {price:800}]));
// { subtotal: 3200, shipping: 500, total: 3700 }
JavaScript拡張アイデア
- 税率を追加して税抜/税込の表示を行う。割引クーポンを適用する仕組みを加える。
時間計算 秒を分秒に変換 toMinSec
コード
function toMinSec(totalSeconds) {
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return { minutes, seconds };
}
JavaScript解説
- 目的: 秒数を分と秒に分解して返す。たとえば 125 秒は 2 分 5 秒。
- Math.floor を使って小数を切り捨て、剰余演算 % で残りの秒を求める。
- 戻り値をオブジェクトにすると取り出しやすい。
テスト
console.log(toMinSec(125)); // { minutes: 2, seconds: 5 }
JavaScript拡張アイデア
- 0埋めで “02:05” 形式にする関数を追加する。
時間計算 時刻に秒を加算 addSecondsToTime
コード
function addSecondsToTime(h, m, s, add) {
let total = h * 3600 + m * 60 + s + add;
// 正規化して 0 から 86399 の範囲にする
total = ((total % 86400) + 86400) % 86400;
const nh = Math.floor(total / 3600);
const nm = Math.floor((total % 3600) / 60);
const ns = total % 60;
const pad = n => String(n).padStart(2, '0');
return `${pad(nh)}:${pad(nm)}:${pad(ns)}`;
}
JavaScript解説
- 目的: 与えた時刻から秒数を足して新しい時刻を “HH:MM:SS” で返す。日をまたぐ場合も正しく処理する。
- 正規化: total を 0~86399 に収めるため ((total % 86400) + 86400) % 86400 というテクニックで負数も正しく扱う。
- padStart で 2 桁表示に整形して見やすくしている。
テスト
console.log(addSecondsToTime(23, 59, 30, 90)); // 00:01:00
JavaScript拡張アイデア
- 日付をまたぐ情報も返す(何日進んだか)ようにする。
文字整形 名前を整形 formatName
コード
function formatName(first = '', last = '') {
if (!first && !last) return '';
if (!first) return last;
if (!last) return first;
return `${last} ${first}`;
}
JavaScript解説
- 目的: 姓と名を受け取り「姓 名」の形式で返す。どちらかが空でも対応する。
- 判定方法: 空文字や undefined は falsy として if (!first) で判定できる。
- 仕様の一例: この実装は last を先に表示する形式。必要なら引数の順序や出力を変更する。
テスト
console.log(formatName('太郎', '山田')); // 山田 太郎
console.log(formatName('太郎')); // 太郎
console.log(formatName(undefined, '佐藤')); // 佐藤
JavaScript拡張アイデア
- ミドルネームや敬称をオプションで受け取る。
文字整形 トリムと大文字変換 cleanText
コード
function cleanText(text, options = { trim: true, upper: false }) {
let result = text;
if (options.trim) result = result.trim();
if (options.upper) result = result.toUpperCase();
return result;
}
JavaScript解説
- 目的: trim や toUpperCase のような一連の整形をオプションでコントロールする。
- オブジェクト引数: オプションをオブジェクトにまとめることで引数の順序に依存しない呼び出しが可能。
- 注意点: options が渡されなかった場合に備えてデフォルトオブジェクトを用意しているが、呼び出し側で partial なオブジェクトを渡すと既定値が置き換わる点に注意。完全にマージしたい場合は Object.assign やスプレッドでマージする。
安全なマージ例
function cleanTextSafe(text, options = {}) {
const opts = Object.assign({ trim: true, upper: false }, options);
let result = text;
if (opts.trim) result = result.trim();
if (opts.upper) result = result.toUpperCase();
return result;
}
JavaScriptテスト
console.log(cleanText(' hello ')); // 'hello'
console.log(cleanText(' hello ', { trim: true, upper: true })); // 'HELLO'
JavaScript拡張アイデア
- 正規表現で複数スペースを 1 にまとめる、特殊文字を除去するなどを追加する。
文字整形 キャメルケース変換 toCamelCase
コード
function toCamelCase(...words) {
if (words.length === 0) return '';
const first = words[0].toLowerCase();
const rest = words.slice(1).map(w => {
const lower = w.toLowerCase();
return lower.charAt(0).toUpperCase() + lower.slice(1);
});
return [first, ...rest].join('');
}
JavaScript解説
- 目的: 複数の語を受け取りキャメルケースに結合する。最初の単語は小文字にする。
- 手順: 最初の単語を小文字化し、残りは先頭だけ大文字にして結合する。toLowerCase で雑な入力の正規化を行う。
- 可変長引数: …words を使うことで単語数が自由に指定可能。
テスト
console.log(toCamelCase('hello', 'WORLD', 'javaScript')); // helloWorldJavascript
JavaScript拡張アイデア
- スペースで分割した文字列を受け取るバージョンを作る。
ゲームのスコア 基本 addScore
コード
function addScore(current, add = 0) {
return current + add;
}
JavaScript解説
- 目的: 現在スコア current に加点 add を足して返す。add を省略できる。
- 単純明快。入力が負の値にならないように呼び出し側で管理するか関数内で検査する。
テスト
console.log(addScore(100)); // 100
console.log(addScore(100, 50)); // 150
JavaScript拡張アイデア
- スコアの上限や下限を設ける。
ゲームのスコア コンボボーナス comboScore
コード
function comboScore(base, combo = 0) {
const multiplier = 1 + combo * 0.1;
return Math.floor(base * multiplier);
}
JavaScript解説
- 目的: コンボ数に応じて倍率を上げる。combo が増えるごとに 10% のボーナスを付与する設計。
- Math.floor で整数化して小数点を切り捨てる。ゲームの仕様により四捨五入や切り上げを使うこともある。
テスト
console.log(comboScore(200, 0)); // 200
console.log(comboScore(200, 3)); // 260
JavaScript拡張アイデア
- 上限倍率を設ける。ボーナスが累積で増える別ルールを実装する。
ゲームのスコア ランキング更新 updateRanking
コード
function updateRanking(ranking, name, score, maxEntries = 5) {
// コピーして破壊的変更を避ける
const newRanking = ranking.slice();
newRanking.push({ name, score });
newRanking.sort((a, b) => b.score - a.score);
return newRanking.slice(0, maxEntries);
}
JavaScript解説
- 目的: 既存ランキングに新しいスコアを追加して上位 maxEntries を返す。
- 破壊的変更回避: 引数の ranking を直接変更すると外側の配列が書き換わってしまうため slice でコピーしてから操作している。
- ソート関数: b.score – a.score により降順ソートを実現している。
テスト
const r = [{name:'A', score:300}, {name:'B', score:250}];
console.log(updateRanking(r, 'C', 280));
// [{name:'A', score:300}, {name:'C', score:280}, {name:'B', score:250}]
JavaScript拡張アイデア
- 同点処理や名前での二次ソートを追加する。永続化する場合はサーバやローカルストレージとの連携を検討する。
最後に学習のコツまとめ
- 小さな関数単位で書いて console.log で期待値を確認する習慣をつける。
- 引数の未指定対策はデフォルト引数か明示的チェックで行う。
- オプションが増えたらオブジェクトで受け取り順序の問題を解決する。
- 可変長引数は「数が可変」な場面で便利。必須の値は普通の引数で受ける。
