JavaScript Tips | 配列ユーティリティ:件数制限

JavaScript JavaScript
スポンサーリンク

何をしたいユーティリティか:「件数制限」

ここでの「件数制限」は、配列の要素数を「最大 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 で配列かどうかを確認し、違ったら空配列を返す。
maxCountnullundefined、マイナスなら「制限なし」とみなしてそのまま返す。
それ以外は 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. 「制限なし」をどう表現するかを統一する

maxCountnull / undefined / マイナスのときにどうするか、を統一しておくと、コードが読みやすくなります。

例えば、「maxCountnull / undefined / 負数なら制限なし」と決めておけば、
どのユーティリティでも同じルールで扱えます。

逆に、「0 をどう扱うか」も決めておく必要があります。

0 件に制限する(常に空になる)。
0 も「制限なし」とみなす。

今回の例では、「0 は 0 件(空配列)」として扱い、
null/undefined/負数 を「制限なし」としました。
ここもプロジェクトのポリシーとして決めておくと、迷いが減ります。

3. 「破壊的」か「非破壊的」かを意識する

limitlimitLast は「新しい配列を返す非破壊的な関数」です。
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 文」から、「意図のはっきりした件数制御」に一段ステップアップしていきます。

タイトルとURLをコピーしました