JavaScript Tips | 配列ユーティリティ:末尾取得

JavaScript JavaScript
スポンサーリンク

何をしたいユーティリティか:「末尾取得」

ここでの「末尾取得」は、配列の「一番最後の要素」や「末尾から数件」を取り出す処理のことです。
英語だと lasttakeRight などと呼ばれます。

業務では、例えば次のような場面でよく使います。
ログの「最新 1 件」だけを見たい。
履歴の「直近 5 件」だけを表示したい。
ソート済みデータの「最大値(最後の 1 件)」を取りたい。

毎回 array[array.length - 1]slice(-n) を直書きしても動きますが、
ユーティリティとして名前を付けておくと、「何をしたいコードなのか」が一気に読みやすくなります。


一番基本:末尾の 1 要素を安全に取得する

素朴な書き方と落とし穴

JavaScript で「末尾の 1 要素」を取る一番素朴な書き方は、これです。

const last = array[array.length - 1];
JavaScript

ただし、業務コードでは次のような問題が出てきます。

arraynullundefined の可能性がある。
array が配列でない可能性がある。
空配列のときに array.length - 1-1 になり、array[-1]undefined になる。

毎回これを意識するのはしんどいので、「安全な末尾取得ユーティリティ」を用意します。

安全な last ユーティリティ

function last(array, defaultValue = undefined) {
  if (!Array.isArray(array) || array.length === 0) {
    return defaultValue;
  }
  return array[array.length - 1];
}
JavaScript

ここが重要ポイントです。

Array.isArray で「本当に配列か」を確認していること。
配列でない、または空配列なら defaultValue を返すこと。
呼び出し側が「空のときに何を返してほしいか」を指定できること。

これにより、「null かもしれない配列」「空かもしれない配列」に対しても、
毎回 if 文を書かずに済みます。

実際の動き

last([1, 2, 3]);              // 3
last([], null);               // null
last(null, "なし");           // "なし"
last(undefined);              // undefined
last("not array", "default"); // "default"
JavaScript

「空のときにどうするか」を defaultValue で決められるのが、実務ではかなり効いてきます。


末尾から複数件を取得する:takeLast 的なユーティリティ

「1 件」ではなく「末尾 N 件」が欲しい場面

ログや履歴、タイムラインなどでは、「末尾から N 件だけ欲しい」という場面が多いです。
素朴には array.slice(-n) で書けますが、
やはり「安全性」と「意図の見やすさ」のためにユーティリティ化しておきます。

takeLast ユーティリティ

function takeLast(array, count) {
  if (!Array.isArray(array)) {
    return [];
  }

  if (count == null) {
    return array.slice();
  }

  if (count <= 0) {
    return [];
  }

  if (array.length <= count) {
    return array.slice();
  }

  return array.slice(array.length - count);
}
JavaScript

ここでの重要ポイントは次の通りです。

配列でなければ空配列を返す。
countnull / undefined なら「制限なし」とみなして全部返す。
count <= 0 のときは空配列を返す。
配列長が count 以下なら、そのまま全部返す。
それ以外は「末尾から count 件」を slice で切り出す。

slice(-count) でも書けますが、「長さを意識したほうが初心者には分かりやすい」ので、あえて length - count で書いています。

実際の動き

const data = [1, 2, 3, 4, 5];

takeLast(data, 2);     // [4, 5]
takeLast(data, 10);    // [1, 2, 3, 4, 5]
takeLast(data, 0);     // []
takeLast(data, -1);    // []
takeLast(data, null);  // [1, 2, 3, 4, 5]
takeLast(null, 3);     // []
JavaScript

「0 以下は 0 件」「null は制限なし」というルールをユーティリティ側で固定しておくと、
呼び出し側が迷わなくなります。


末尾取得を業務でどう使うか

例1:ログの「最新 1 件」を見る

ログ配列が時系列順(古い → 新しい)で積み上がっているとします。
「最新ログだけを見たい」場合、こう書けます。

const latest = last(logs, null);

if (!latest) {
  // ログがないときの処理
} else {
  // latest を使った処理
}
JavaScript

last を使うことで、「末尾の要素を取っている」という意図がコードから読み取れます。
logs && logs[logs.length - 1] のような書き方より、ずっと意味が明確です。

例2:履歴の「直近 N 件」だけを表示する

ユーザーの操作履歴などを、「直近 10 件だけ」画面に出したい場合。

const recent = takeLast(history, 10);
JavaScript

これだけで、「末尾から 10 件だけ」を安全に取り出せます。
slice(-10) をあちこちに書くより、「意図が名前に出ている」ほうが読みやすいです。

例3:ソート後の「最大値」を取る

例えば、「価格の安い順にソートして、最も高い商品だけを表示したい」場合。

const sorted = [...products].sort((a, b) => a.price - b.price);
const mostExpensive = last(sorted, null);
JavaScript

「ソート後の末尾要素=最大値」というパターンはよく出てきます。
ここでも last を使うことで、「最後を取っている」ことがはっきりします。


「末尾取得」ユーティリティ設計で意識してほしいポイント

1. 「空のとき」の扱いを毎回決めなくてよいようにする

先頭取得と同じく、末尾取得で一番面倒なのは、「空配列だったときどうするか?」を毎回考えることです。

undefined をそのまま返すのか。
null を返すのか。
特定のデフォルト値を返すのか。

last(array, defaultValue) のように、「空のときは defaultValue を返す」と決めておけば、
呼び出し側は「空なら null でいい」「空なら 0 でいい」など、用途に応じて選べます。

ここをユーティリティに閉じ込めることで、
「毎回 if (!array || array.length === 0) と書く地味なストレス」から解放されます。

2. 「配列でないもの」が来ても壊れないようにする

現実の業務コードでは、「本当は配列のはずだけど、何かの拍子に null やオブジェクトが来る」ことが普通にあります。
そこで落ちると、ユーザーから見て「たまにだけ落ちる謎のバグ」になります。

Array.isArray でチェックし、配列でなければ「空扱い」にしてしまうのは、
「壊れないこと」を優先した設計です。

takeLast も同様に、「配列でなければ空配列」を返すようにしておくと、
呼び出し側は「とりあえず配列として扱ってよい」という前提で書けます。

3. 「先頭取得」と対になる API にしておく

firstlasttaketakeLast のように、
「先頭系」と「末尾系」をペアで用意しておくと、API 設計としてとても気持ちよくなります。

first / last … 1 要素だけ欲しい。
take / takeLast … 先頭/末尾から N 件欲しい。

名前と挙動が対称になっていると、
「先頭でこれができるなら、末尾も同じ感じで書けるよね」と直感的に分かります。


少し手を動かして感覚をつかむ

コンソールで、次のようなコードを実際に打ってみてください。

const data = [1, 2, 3, 4, 5];

last(data);
last([], "empty");
last(null, "none");

takeLast(data, 2);
takeLast(data, 10);
takeLast(data, 0);
takeLast(null, 3);

const logs = [
  { at: "10:00", msg: "A" },
  { at: "10:05", msg: "B" },
  { at: "10:10", msg: "C" },
];

const latest = last(logs, null);
const recentTwo = takeLast(logs, 2);
JavaScript

「空のときに何が返ってくるか」「末尾 N 件がどう切り出されるか」「ログの最新がどう取れるか」を、自分の目で確認してみてください。

そのうえで、自分のプロジェクトに

export function last(...) { ... }
export function takeLast(...) { ... }
JavaScript

のような関数を置き、「配列の末尾を取りたくなったら、必ずこの“末尾取得ユーティリティ”を通す」というルールを作ってみてください。
そうすると、「場当たり的な array[array.length - 1]」から、「意図と安全性を兼ね備えた末尾取得」に一段ステップアップしていきます。

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