JavaScript | 1 日 120 分 × 7 日アプリ学習:検索 & ソート機能付き一覧

APP JavaScript
スポンサーリンク

3日目のゴールと今日やること

3日目のテーマは
「検索 × ソート × フィルタを“実務レベル”で組み合わせて、より高度な一覧アプリを作る」
ことです。

1日目で map / filter / sort の基礎、
2日目で state を使った検索・ソート・フィルタの統合処理を学びました。

3日目では、さらに一歩進んで

  • タグフィルタ(複数条件の AND / OR)
  • ソート条件の切り替え(名前順・年齢順など)
  • ページネーション(一覧を分割して表示)
  • UI とロジックの分離をより強固にする

といった「実務でよく使う一覧機能」を作っていきます。

今日のキーワードは
「複数条件の組み合わせ」
「非破壊操作の徹底」
です。


タグフィルタを作る(複数条件 AND / OR)

例:ユーザーにタグを付ける

const users = [
  { id: 1, name: "Alice", age: 24, tags: ["frontend", "remote"] },
  { id: 2, name: "Bob", age: 30, tags: ["backend"] },
  { id: 3, name: "Charlie", age: 28, tags: ["frontend", "design"] },
  { id: 4, name: "David", age: 35, tags: ["remote"] },
  { id: 5, name: "Eve", age: 22, tags: ["design"] }
];
JavaScript

UI のイメージ

<label><input type="checkbox" class="tag" value="frontend" /> Frontend</label>
<label><input type="checkbox" class="tag" value="backend" /> Backend</label>
<label><input type="checkbox" class="tag" value="design" /> Design</label>
<label><input type="checkbox" class="tag" value="remote" /> Remote</label>

JavaScript 側で取得します。

const tagCheckboxes = document.querySelectorAll(".tag");
JavaScript

タグフィルタのロジック(AND / OR の違い)

OR 条件(どれか 1 つでも含む)

function filterByTagsOR(arr, selectedTags) {
  if (selectedTags.length === 0) return arr;

  return arr.filter(user =>
    user.tags.some(tag => selectedTags.includes(tag))
  );
}
JavaScript

AND 条件(すべて含む)

function filterByTagsAND(arr, selectedTags) {
  if (selectedTags.length === 0) return arr;

  return arr.filter(user =>
    selectedTags.every(tag => user.tags.includes(tag))
  );
}
JavaScript

深掘りポイント

  • some → OR 条件
  • every → AND 条件

この 2 つを使い分けるだけで、
複雑なタグ検索が簡単に作れます。


state にタグフィルタを追加する

const state = {
  keyword: "",
  sort: "none",
  minAge: null,
  maxAge: null,
  tags: [],
  tagMode: "OR" // OR または AND
};
JavaScript

タグチェックボックスのイベントをつなぎます。

tagCheckboxes.forEach(cb => {
  cb.addEventListener("change", () => {
    const selected = [...tagCheckboxes]
      .filter(c => c.checked)
      .map(c => c.value);

    updateState({ tags: selected });
  });
});
JavaScript

applyFilters にタグフィルタを統合する

function applyFilters() {
  let result = [...users];

  if (state.keyword) {
    const lower = state.keyword.toLowerCase();
    result = result.filter(user =>
      user.name.toLowerCase().includes(lower)
    );
  }

  if (state.minAge !== null) {
    result = result.filter(user => user.age >= state.minAge);
  }

  if (state.maxAge !== null) {
    result = result.filter(user => user.age <= state.maxAge);
  }

  if (state.tags.length > 0) {
    if (state.tagMode === "OR") {
      result = filterByTagsOR(result, state.tags);
    } else {
      result = filterByTagsAND(result, state.tags);
    }
  }

  if (state.sort === "asc") {
    result = [...result].sort((a, b) => a.age - b.age);
  }

  if (state.sort === "desc") {
    result = [...result].sort((a, b) => b.age - a.age);
  }

  renderList(result);
}
JavaScript

深掘りポイント

applyFilters が
検索 → 年齢 → タグ → ソート → 表示
という「処理の流れ」を完全に管理しています。

これが中級者のコードです。


ソート条件を増やす(名前順・年齢順)

state に sortKey を追加

const state = {
  keyword: "",
  sort: "none",
  sortKey: "age", // "age" or "name"
  minAge: null,
  maxAge: null,
  tags: [],
  tagMode: "OR"
};
JavaScript

ソートロジックを拡張

if (state.sort !== "none") {
  const key = state.sortKey;

  result = [...result].sort((a, b) => {
    if (state.sort === "asc") {
      return a[key] > b[key] ? 1 : -1;
    } else {
      return a[key] < b[key] ? 1 : -1;
    }
  });
}
JavaScript

深掘りポイント

sortKey を変えるだけで
「名前順」「年齢順」などの切り替えが可能になります。


ページネーションを追加する(一覧を分割表示)

state にページ情報を追加

const state = {
  ...,
  page: 1,
  perPage: 3
};
JavaScript

ページネーション処理

function paginate(arr, page, perPage) {
  const start = (page - 1) * perPage;
  return arr.slice(start, start + perPage);
}
JavaScript

applyFilters の最後に追加

const paginated = paginate(result, state.page, state.perPage);
renderList(paginated);
JavaScript

深掘りポイント

slice は非破壊なので、
元の配列を壊さずにページ分割できます。


ページボタンを UI とつなぐ

prevButton.addEventListener("click", () => {
  if (state.page > 1) {
    updateState({ page: state.page - 1 });
  }
});

nextButton.addEventListener("click", () => {
  updateState({ page: state.page + 1 });
});
JavaScript

3日目のまとめ

今日あなたがやったことを整理すると、こうなります。

  • タグフィルタ(OR / AND)を実装
  • state にタグ情報を追加
  • applyFilters にタグ処理を統合
  • ソート条件を「名前順」「年齢順」に拡張
  • ページネーションを追加
  • UI とロジックを完全に分離
  • filter / sort / slice を組み合わせて高度な一覧処理を実現

どれも実務で頻繁に使うテクニックです。


今日いちばん深く理解してほしいこと

3日目の本質は、

「検索・ソート・フィルタは、map / filter / sort / slice を組み合わせることで“無限に拡張できる”」

ということです。

タグ検索
複合条件
ページネーション
ソート切り替え

これらはすべて
高階関数の組み合わせ
で作れます。

4日目では、この一覧アプリに

  • デバウンス(検索の負荷軽減)
  • ハイライト表示
  • ソートアイコンの切り替え
  • 状態管理の整理(state の分割)

などを加えて、さらに“プロ仕様の一覧アプリ”に育てていきます。

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