かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

リストの中から特定の index の前後を n 件の範囲のデータを取得したい。

Pagination みたいにリストの中から常に n 件の値を取得する方法を考えてみた。(車輪の再発明)

要件

  • リストが n 件以上ある時は 常に n 件 表示する
  • current を中心として前後に n / 2 件づつの範囲を取る
  • 偶数件の範囲を取る場合は後ろの方を 1 件多くする
  • current が前後に n / 2 件取れない場合は先頭から又は最後から n 件取得する

current を中心に n 件 の範囲をとる

偶数件の範囲を取る場合は後ろを 1 件多くする

current を中心にできない場合は先頭から n 件の範囲に

current を中心にできない場合は最後から n 件の範囲に

実装イメージ

完成イメージ

Sample

See the Pen Untitled by KIKIKI (@kikiki_kiki) on CodePen.


current の index から開始と終了の index を求める

リストから抽出するので Array.slice() を使う想定で開始 start と 終了 end (範囲の index + 1) を取得できれば良い

type GetSliceRangeProps = {
  itemLength: number;
  currentIndex: number;
  displayNum: number;
};

const getSliceRange = ({
  itemLength,
  currentIndex,
  displayNum
}: GetSliceRangeProps): {
  start: unumber;
  end?: number;
} => {
  // 表示数がリストより多い場合は全件表示する
  if (displayNum > itemLength) {
    return {
      start: 0,
    };
  }
  // current の前後に表示する要素数
  // 表示数が偶数の場合、前が 1つ少なくなるので Math.floor で少ない数に合わせる
  const half = Math.floor((displayNum - 1) / 2);

  // current が前に必要な数に満たない場合は、先頭から表示件数分を表示する
  if (currentIndex < half) {
    return {
      start: 0,
      end: displayNum,
    };
  }

  // currentIndex + 後ろに必要な数 がリスト長以上になってしまう場合はリストの後ろから表示件数分を表示する
  // index は 0 からで、リストの要素数 length は 1 からのカウントなのでリストの最後の要素の index と比較する
  const lastItemIndex = itemLength - 1;
  if (currentIndex + half >= lastItemIndex) {
    return {
      start: itemLength - displayNum
    };
  }
  
  const start = currentIndex - half;
  const end = start + displayNum;
  
  return {
    start,
    end,
  };
};

// 使い方
const {start, end} = getSliceRange({
  itemLength: MyArray.length,
  currentIndex,
  displayNum
});

MyArray.slice(start, end);

index と length 混ざってると混乱しがち。
おわり


[参考]