🙄

JavaScriptでネストしたオブジェクトから、キーワード検索

2024/08/19に公開2

ネストしたオブジェクトから値を取得するにはどうするのか?

仕事で配列を操作して値を取得するロジックを実装する場面があった。意外と難しかった。入れ子構造になってるオブジェクトから値をキーワード検索で取得する。

let companies = [
    {
        id: 1,
        name: '株式会社A',
        member: {
            age: 20,
            count: 500,
        },
        place: 'TOKYO'
    },
    {
        id: 2,
        name: '株式会社B',
        member: {
            age: 25,
            count: 300,
        },
        place: 'OSAKA'
    },
    {
        id: 3,
        name: '株式会社C',
        member: {
            age: 20,
            count: 500,
        },
        place: 'TOKYO'
    }
];

const filterByKeyword = (companies, keyword) => {
    return companies.filter(company => {
        return Object.values(company).some(value => {
            if (typeof value === 'object') {
                return Object.values(value).includes(keyword);
            }
            return value === keyword;
        });
    });
};

let result = filterByKeyword(companies, 'TOKYO');
console.log(result);

result = filterByKeyword(companies, 20);
console.log(result);

このコードでは、以下の手順を踏んでいます:

  1. 会社のデータを配列として管理します。
  2. filterByKeyword 関数を定義し、指定したキーワードに基づいて会社をフィルタリングします。
  3. filterByKeyword 関数を呼び出し、TOKYO または 20 に一致する会社を検索して結果をログに出力します。
    この方法で、指定したキーワードに一致する会社の情報を抽出できます。

こちらのメソッドの知識が必要だった。公式の解説を読んでもらった方がいいと思うので、解説はしません。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/some

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

filterの書き方にもパターンがあった

2個のパターン。これは良くないらしい?
1個にまとめた方がいいとか。

// ネストしたオブジェクト
let account = {
    name: "Mike",
    age: 25,
    expenses: {
      food: 100,
      rent: 300,
      insurance: 200,
    },
    income: 1000,
  };
  
  const searchFitter = (account, key, minValue) => {
    // expensesオブジェクトを配列に変換
    const expensesArray = Object.entries(account.expenses);
    
    // filterをドットで繋げて使用
    const result = expensesArray
      .filter(([k, v]) => k === key)
      .filter(([k, v]) => v > minValue);
    
    // 結果をオブジェクトに戻す
    const filteredExpenses = Object.fromEntries(result);
    return filteredExpenses;
  };
  
  let result = searchFitter(account, "rent", 200);
  console.log(result); // { rent: 300 }

1個にまとめたパターン。仕事のコードは丸かっこで囲んだのがあったので、&&とかで、2つ処理かけなかったような???

// ネストしたオブジェクト 
let account = {
    name: 'Mike',
    age: 25,
    expenses: {
        food: 100,
        rent: 300,
        insurance: 200
    },
    income: 1000
};

const searchFitter = (account, key, value) => {
    const expensesArray = Object.entries(account.expenses);
    expensesArray.filter(([key, value]) => key === 'rent' && value > 200);
    const filteredExpenses = Object.fromEntries(result);
    return filteredExpenses;
};

// expensesオブジェクトを配列に変換
const expensesArray = Object.entries(account.expenses);

// filterを1回だけ使用して、指定した条件に一致する項目を抽出
const result = expensesArray.filter(([key, value]) => key === 'rent' && value > 200);

// 結果をオブジェクトに戻す
const filteredExpenses = Object.fromEntries(result);

console.log(filteredExpenses); // { rent: 300 }

Reactでも配列の検索ができる機能を作ってみた。意外と難しいですね。もっと複雑なロジックを実装していた気がする。

import React, { useState, useEffect } from 'react';
import { FaSearch } from 'react-icons/fa'; // react-icons ライブラリを使用

interface Company {
  id: number;
  name: string;
  member: {
    age: number;
    count: number;
  };
  place: string;
}

const companies: Company[] = [
  {
    id: 1,
    name: '株式会社A',
    member: {
      age: 30,
      count: 100,
    },
    place: 'OSAKA'
  },
  {
    id: 2,
    name: '株式会社B',
    member: {
      age: 25,
      count: 200,
    },
    place: 'KYOTO'
  },
  {
    id: 3,
    name: '株式会社C',
    member: {
      age: 20,
      count: 500,
    },
    place: 'TOKYO'
  }
];

const filterByKeyword = (companies: Company[], keyword: string | number): Company[] => {
  return companies.filter(company => {
    return Object.values(company).some(value => {
      if (typeof value === 'object' && value !== null) {
        return Object.values(value).some(subValue => {
          if (typeof subValue === 'string' || typeof subValue === 'number') {
            return subValue.toString().includes(keyword.toString());
          }
          return false;
        });
      }
      if (typeof value === 'string' || typeof value === 'number') {
        return value.toString().includes(keyword.toString());
      }
      return false;
    });
  });
};

const Filter: React.FC = () => {
  const [keyword, setKeyword] = useState<string | number>('');
  const [filteredCompanies, setFilteredCompanies] = useState<Company[]>(companies);

  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Enter') {
        setFilteredCompanies(filterByKeyword(companies, keyword));
      }
    };
    // Enter キーを押したときに検索を実行
    window.addEventListener('keydown', handleKeyDown);
    return () => {
      window.removeEventListener('keydown', handleKeyDown);
    };
  }, [keyword]);

  const handleSearchClick = () => {
    setFilteredCompanies(filterByKeyword(companies, keyword));
  };

  return (
    <div>
      <input
        type="text"
        value={keyword}
        onChange={(e) => setKeyword(e.target.value)}
        placeholder="キーワードを入力"
      />
      <button onClick={handleSearchClick}>
        <FaSearch />
      </button>
      <ul>
        {filteredCompanies.map(company => (
          <li key={company.id}>
            {company.name} - {company.place}
            {company.member.age}{company.member.count}</li>
        ))}
      </ul>
    </div>
  );
};

export default Filter;

感想

配列の操作は難しいですね😅
他にも検索する方法は複数あります。リンク載せておきます。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/find
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex

Discussion

JboyHashimotoJboyHashimoto

普通のJavaScriptの場合

検索機能を作ってみた。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form id="searchForm">
        <input type="text" id="keyword" placeholder="Enter keyword">
        <button type="submit">Search</button>
    </form>
    <div id="result"></div>

    <script>
        const jsonData = [
            {
                "id": 1,
                "name": "Leanne Graham",
                "tag": {
                    "label": "1111"
                },
            },
            {
                "id": 2,
                "name": "Ervin Howell",
                "tag": {
                    "label": "2222"
                },
            },
            {
                "id": 3,
                "name": "Clementine Bauch",
                "tag": {
                    "label": "ffffff"
                },
            },
            {
                "id": 4,
                "name": "Patricia Lebsack",
                "tag": {
                    "label": "4444"
                },
            },
            {
                "id": 5,
                "name": "Chelsey Dietrich",
                "tag": {
                    "label": "5555"
                },
            },
            {
                "id": 6,
                "name": "Mrs. Dennis Schulist",
                "tag": {
                    "label": "qqqqq"
                },
            },
            {
                "id": 7,
                "name": "Kurtis Weissnat",
                "tag": {
                    "label": "7777"
                },
            },
            {
                "id": 8,
                "name": "Nicholas Runolfsdottir V",
                "tag": {
                    "label": "8888"
                },
            },
            {
                "id": 9,
                "name": "Glenna Reichert",
                "tag": {
                    "label": "9999"
                },
            },
            {
                "id": 10,
                "name": "Clementina DuBuque",
                "tag": {
                    "label": "1010"
                },
            }
        ];

        /**
         * この行のコードは、item.tag オブジェクトの値の中に、指定された keyword が含まれているかどうかをチェックしています。以下に詳細を説明します。

        詳細な説明
        Object.values(item.tag):

        item.tag オブジェクトのすべての値を配列として取得します。
        例えば、item.tag が { "label": "1111" } の場合、Object.values(item.tag) は ["1111"] になります。
        .some(value => value.includes(keyword)):

        some メソッドは、配列の少なくとも1つの要素が条件を満たすかどうかをチェックします。
        ここでは、各値 (value) が keyword を含んでいるかどうかをチェックしています。
        value.includes(keyword) は、文字列 value が keyword を含んでいる場合に true を返します。
        return:

        some メソッドが true を返す場合、つまり少なくとも1つの値が keyword を含んでいる場合に true を返します。
        そうでない場合は false を返します
         **/

        function getJsonData(jsonData, keyword) {
            const result = jsonData.filter(item => {
                return Object.values(item.tag).some(value => value.includes(keyword));
            });
            return result.length > 0 ? result : '該当なし';
        }

        document.getElementById('searchForm').addEventListener('submit', function(event) {
            event.preventDefault();
            const keyword = document.getElementById('keyword').value;
            const result = getJsonData(jsonData, keyword);
            document.getElementById('result').innerHTML = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
        });
    </script>
</body>
</html>
JboyHashimotoJboyHashimoto

TypeScrtiptの場合

node.jsで実行するだけですが、データ型をつけた方が、どのデータ型なのかコードジャンプで確認できるので、便利でしたね。そのままのJavaScriptだと、エラーとか出ませんし😅

const jsonData = [
    {
        "id": 1,
        "name": "Leanne Graham",
        "tag": {
            "label": "1111"
        },
    },
    {
        "id": 2,
        "name": "Ervin Howell",
        "tag": {
            "label": "2222"
        },
    },
    {
        "id": 3,
        "name": "Clementine Bauch",
        "tag": {
            "label": "ffffff"
        },
    },
    {
        "id": 4,
        "name": "Patricia Lebsack",
        "tag": {
            "label": "4444"
        },
    },
    {
        "id": 5,
        "name": "Chelsey Dietrich",
        "tag": {
            "label": "5555"
        },
    },
    {
        "id": 6,
        "name": "Mrs. Dennis Schulist",
        "tag": {
            "label": "qqqqq"
        },
    },
    {
        "id": 7,
        "name": "Kurtis Weissnat",
        "tag": {
            "label": "7777"
        },
    },
    {
        "id": 8,
        "name": "Nicholas Runolfsdottir V",
        "tag": {
            "label": "8888"
        },
    },
    {
        "id": 9,
        "name": "Glenna Reichert",
        "tag": {
            "label": "9999"
        },
    },
    {
        "id": 10,
        "name": "Clementina DuBuque",
        "tag": {
            "label": "1010"
        },
    }
];

// data type
interface Company {
    id: number;
    name: string;
    tag: {
        label: string;
    };
}

function getJsonData(jsonData: Company[], keyword: string): Company[] | string {
    const result = jsonData.filter(item => {
        return Object.values(item.tag).some(value => value.includes(keyword));
    });
    return result.length > 0 ? result : "No result found" as string;
}

console.log(getJsonData(jsonData, "1111"));