何をしたいユーティリティか:「件数制限」
ここでの「件数制限」は、配列の要素数を「最大 N 件まで」に制御する処理のことです。
イメージとしては「取りすぎない」「持ちすぎない」ためのブレーキです。
業務では、例えばこんな場面で使います。
検索結果を「最大 100 件まで」に制限したい。
ログを「最新 1000 件だけ」保持して、それより古いものは捨てたい。
ユーザーが選択できる項目数を「最大 5 件まで」にしたい。
この「件数制限」をユーティリティとして持っておくと、
あちこちで同じような slice(0, N) や if (length > N) を書かずに済み、
「どこで、どんなルールで制限しているか」がはっきりします。
基本形:先頭から N 件だけに制限する
一番シンプルな limit 関数
まずは、「配列の先頭から最大 N 件だけを残す」基本形です。
function limit(array, maxCount) {
if (!Array.isArray(array)) {
return [];
}
if (maxCount == null || maxCount < 0) {
return array.slice();
}
return array.slice(0, maxCount);
}
JavaScriptここでのポイントをかみ砕きます。
Array.isArray で配列かどうかを確認し、違ったら空配列を返す。maxCount が null や undefined、マイナスなら「制限なし」とみなしてそのまま返す。
それ以外は slice(0, maxCount) で先頭から最大 maxCount 件だけを取り出す。
slice は「元の配列を壊さず、新しい配列を返す」ので、安全に使えます。
実際の動き
const data = [1, 2, 3, 4, 5];
limit(data, 3); // [1, 2, 3]
limit(data, 10); // [1, 2, 3, 4, 5]
limit(data, 0); // []
limit(data, -1); // [1, 2, 3, 4, 5] (制限なし扱い)
JavaScript「とりあえず先頭から N 件だけ欲しい」という場面では、この limit だけで十分です。
最新 N 件だけを残す:末尾側の件数制限
ログや履歴でよくある「最新だけ残したい」
ログや履歴では、「古いものは捨てて、最新 N 件だけ残したい」という要件がよくあります。
この場合は、「末尾から N 件」を取り出すユーティリティがあると便利です。
function limitLast(array, maxCount) {
if (!Array.isArray(array)) {
return [];
}
if (maxCount == null || maxCount < 0) {
return array.slice();
}
if (array.length <= maxCount) {
return array.slice();
}
return array.slice(array.length - maxCount);
}
JavaScriptここでの重要ポイントは、「長さが maxCount 以下なら、そのまま返す」ことです。
無駄な slice を避ける意味もありますが、「仕様として“足りない分はそのまま全部返す”」と決めているのが大事です。
実際の動き
const logs = ["L1", "L2", "L3", "L4", "L5"];
limitLast(logs, 2); // ["L4", "L5"]
limitLast(logs, 10); // ["L1", "L2", "L3", "L4", "L5"]
JavaScript「最新 N 件だけを保持したい」「画面には最新 50 件だけ出したい」といった場面で、そのまま使えます。
「追加するときに」件数制限を守るユーティリティ
「溜まりすぎないようにする」発想
単に limit で切り詰めるだけでなく、
「配列に要素を追加するときに、常に最大件数を超えないようにする」ユーティリティもよく使います。
例えば、「最新 100 件だけ保持するログ配列」を考えます。
function pushWithLimit(array, item, maxCount) {
if (!Array.isArray(array)) {
array = [];
}
array.push(item);
if (typeof maxCount === "number" && maxCount >= 0 && array.length > maxCount) {
const overflow = array.length - maxCount;
array.splice(0, overflow);
}
return array;
}
JavaScriptここが重要ポイントです。
新しい要素を push で末尾に追加する。maxCount を超えていたら、「先頭から」余分な件数だけ splice で削る。
結果として、「常に末尾側に最新データが残る」状態を保つ。
これは「破壊的」な関数です(元の配列を書き換えます)。
ログバッファやキューのような用途では、この「破壊的更新」がむしろ自然です。
実際の動き
let logs = [];
logs = pushWithLimit(logs, "L1", 3); // ["L1"]
logs = pushWithLimit(logs, "L2", 3); // ["L1", "L2"]
logs = pushWithLimit(logs, "L3", 3); // ["L1", "L2", "L3"]
logs = pushWithLimit(logs, "L4", 3); // ["L2", "L3", "L4"]
logs = pushWithLimit(logs, "L5", 3); // ["L3", "L4", "L5"]
JavaScript常に「最新 3 件だけ」が残り、それより古いものは自動的に捨てられています。
選択数の制限:「これ以上選べません」を実装する
UI でよくある「最大 5 件まで選択可能」
チェックボックスやタグ選択などで、「最大 N 件までしか選べない」という要件もよくあります。
このとき、「選択済み配列」に対して件数制限をかけるユーティリティを用意しておくと、ロジックが整理されます。
function canAddWithLimit(selected, maxCount) {
if (!Array.isArray(selected)) {
return true;
}
if (maxCount == null || maxCount < 0) {
return true;
}
return selected.length < maxCount;
}
JavaScriptこれは「今の選択数で、まだ追加してよいか?」だけを判定する関数です。
実際の使い方イメージ
let selected = [];
function toggleSelect(id, maxCount) {
const index = selected.indexOf(id);
if (index >= 0) {
selected = selected.filter((x) => x !== id);
return;
}
if (!canAddWithLimit(selected, maxCount)) {
alert(`最大 ${maxCount} 件までしか選べません`);
return;
}
selected = [...selected, id];
}
JavaScript「件数制限のルール」は canAddWithLimit に閉じ込めておき、
UI 側は「ダメならアラートを出す」という役割に集中できます。
件数制限で特に意識してほしい重要ポイント
1. 「制限を超えたとき」の挙動をはっきり決める
件数制限には、いくつかのパターンがあります。
超えた分を切り捨てる(limit のように「先頭 N 件だけ」)。
古いものから捨てる(pushWithLimit のように「先頭を削る」)。
そもそも追加させない(canAddWithLimit のように「追加前に拒否する」)。
どれが正しいかは、業務要件によって変わります。
ユーティリティを作るときは、「この関数はどのパターンなのか」を名前と挙動で明確にしておくことが大事です。
2. 「制限なし」をどう表現するかを統一する
maxCount が null / undefined / マイナスのときにどうするか、を統一しておくと、コードが読みやすくなります。
例えば、「maxCount が null / undefined / 負数なら制限なし」と決めておけば、
どのユーティリティでも同じルールで扱えます。
逆に、「0 をどう扱うか」も決めておく必要があります。
0 件に制限する(常に空になる)。
0 も「制限なし」とみなす。
今回の例では、「0 は 0 件(空配列)」として扱い、null/undefined/負数 を「制限なし」としました。
ここもプロジェクトのポリシーとして決めておくと、迷いが減ります。
3. 「破壊的」か「非破壊的」かを意識する
limit や limitLast は「新しい配列を返す非破壊的な関数」です。pushWithLimit は「元の配列を書き換える破壊的な関数」です。
どちらも有用ですが、混ざるとバグの原因になります。
配列を共有している場面では、基本は「非破壊」を選ぶ。
バッファやキューのように「1 箇所でだけ管理する配列」なら、「破壊的」でもよい。
このあたりの方針を、ユーティリティの名前(pushWithLimit など)とコメントで伝えておくと、
チーム開発でも安心して使えます。
少し手を動かして感覚をつかむ
コンソールで、次のようなコードを実際に打ってみてください。
const data = [1, 2, 3, 4, 5];
limit(data, 3);
limitLast(data, 2);
let logs = [];
logs = pushWithLimit(logs, "L1", 3);
logs = pushWithLimit(logs, "L2", 3);
logs = pushWithLimit(logs, "L3", 3);
logs = pushWithLimit(logs, "L4", 3);
let selected = [];
canAddWithLimit(selected, 2); // true
selected = [...selected, 1];
canAddWithLimit(selected, 2); // true
selected = [...selected, 2];
canAddWithLimit(selected, 2); // false
JavaScript「どのタイミングで何件に制限されているか」「古いものがどう捨てられているか」「追加が拒否される条件は何か」を、自分の目で確認してみてください。
そのうえで、自分のプロジェクトに
export function limit(...) { ... }
export function limitLast(...) { ... }
export function pushWithLimit(...) { ... }
export function canAddWithLimit(...) { ... }
JavaScriptのような関数を置き、「配列の件数を制御したくなったら、必ずこの“件数制限ユーティリティ”を通す」というルールを作ってみてください。
そうすると、「場当たり的な slice と if 文」から、「意図のはっきりした件数制御」に一段ステップアップしていきます。
