【React】配列の重複チェックや重複削除して、ユニークな値を抽出する方法

2023年10月18日React

Reactに限った話ではありませんが、ネイティブのJavaScriptのSetを使って配列の重複チェックや削除して、ユニークな値だけを取り出す方法を紹介します。
本記事では、Reactのプロジェクトでよくある「オブジェクト配列の中からカテゴリー名を抽出し、ユニークなリストを作成する」ケースを例に、Setの活用方法を紹介します。
そもそもSetとは何かについては、以下の記事を参考にしてみてください。

JavaScriptのSetを使って配列の重複削除して、ユニークな値だけを取り出す方法

たとえば、以下のようなデータから、categoryの値を一意のユニークな値のみの配列にしたいとします。

const data = [
    {
        id: 1,
        category: 'カテゴリ1',
        image: Image9,
        title: "タイトル1",
    },
    {
        id: 2,
        category: 'カテゴリ1',
        image: Image9,
        title: "タイトル2",
    },
    {
        id: 3,
        category: 'カテゴリ2',
        image: Image9,
        title: "タイトル3",
    },
    {
        id: 4,
        category: 'カテゴリ2',
        image: Image9,
        title: "タイトル4",
    },
    {
        id: 5,
        category: 'カテゴリ3',
        image: Image9,
        title: "タイトル5",
    },
    {
        id: 6,
        category: 'カテゴリ4',
        image: Image9,
        title: "タイトル6",
    },
]

Setを使うことで、重複したカテゴリー名を簡単に取り除き、一意のリストを取得できます。

const categories = data.map(item => item.category);
const uniqueCategories = [...new Set(categories)];

console.log(uniqueCategories); 
// Set(4) {'カテゴリ1', 'カテゴリ2', 'カテゴリ3', 'カテゴリ4'}

Set のままだと配列ではないので、スプレッド構文 ([…new Set()]) を使って配列に変換しています。

重複削除したユニークなデータに新しい値を追加したい場合

たとえば、カテゴリー一覧に"all"などを付け足したい場合があります。
Set でユニークな値を取得した後、新たに “all" という項目を追加したい場合は、スプレッド構文を使って配列に変換しながら追加できます。

  const uniqueCategories = ["all", ...new Set(categories)];
  console.log(uniqueCategories);
// (5) ['all', 'カテゴリ1', 'カテゴリ2', 'カテゴリ3', 'カテゴリ4']

重複削除したユニークな値のみを抽出する方法は、他にもいくつかありますが、
Setを使うと最もシンプルなコードになります。

Reactでカテゴリーをフィルターしたデータを表示したい場合

import { useState } from "react";
import data from "./data";
import ProjectCategories from "./ProjectCategories";
import Projects from "./Projects";

const Sample = () => {
  const [projects, setProjects] = useState(data);
  const categories = data.map((item) => item.category);
  const uniqueCategories = ["all", ...new Set(categories)];

  const filterProjectHandler = (category) => {
    if (category === "all") {
      setProjects(data);
      return;
    }
    const filterProjects = data.filter(
      (project) => project.category === category
    );
    setProjects(filterProjects);
  };

  return (
    <section>
      <div className="container">
        <ProjectCategories categories={uniqueCategories} onFilterProjects={filterProjectHandler} />
        <Projects projects={projects} />
      </div>
    </section>
  );
};

export default Sample;

重複削除したユニークな値のみのカテゴリーデータにしてから、
filter() を使い、選択されたカテゴリーに一致するデータのみを抽出して、useState で管理している projects を更新します。
“all" が選択された場合は、すべてのデータを表示するようにします。
上のケースの"all"の場合などすべてを表示したいとき、更新用関数に全データをセットしてあげることで初期状態と同じにすることができます。

ProjectCategories(フィルダーする子コンポーネント)

import React, { useState } from "react";

const ProjectCategories = ({ categories, onFilterProjects }) => {
  const [activeCategory, setActiveCategory] = useState("all");
  const changeCategoryHandler = (activeCat) => {
    setActiveCategory(activeCat); // カテゴリーをセット
    onFilterProjects(activeCat); // そのカテゴリーでフィルター
  };
  return (
    <div className="portfolio__categories">
      {categories.map((category) => (
        <button key={category} className={`btn cat__btn ${
          activeCategory === category ? "primary" : "white"
        } `}
        onClick={() => changeCategoryHandler(category)}>{category}</button>
      ))}
    </div>
  );
};

export default ProjectCategories;

ProjectCategories コンポーネントでは、カテゴリーごとにボタンを作成し、クリックされたカテゴリーに応じてフィルタリングを行います。
セットする処理内容は、ボタンがクリックされたら
カテゴリーをそのボタンのカテゴリーにセットすることと
そのカテゴリーでカードをフィルターすること
の2点あります。
どちらもクリックしたカテゴリーが引数に必要なので、useStateで状態を作ってあげて、
それをクリックされたカテゴリー(activeCategory)の更新用関数にセットします。
そして、propsで渡ってきたフィルターの関数にクリックされたカテゴリーの情報を引数として渡してあげればフィルター機能が完成です。

クリックされたカテゴリーだけ見た目を変える

className={`btn cat__btn ${
          activeCategory === category ? "primary" : "white"
        } `}

クリックされたカテゴリーだけ見た目を変える場合、クリックされたカテゴリー(activeCategory)を条件分岐に利用して動的にcssのクラスを付与してあげればOKです。
たとえばactiveCategory を useState で管理し、選択されたボタンに primary クラス、それ以外のボタンに white クラスを適用します。

React

Posted by devsakaso