🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

654閲覧

絞り込み機能をリファクタリングしたい

hello_space

総合スコア24

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2020/12/30 15:18

reactとlaravelで出力する値を絞り込む機能を作成しています。

javascript

1 2const [data1, setData1] = useState(0) 3const [data2, setData2] = useState(0) 4const selectedDatas = Datas.filter(row => { 5 if(data1 == 0 && data2 == 0) { 6 return true 7 } 8 if(data1 != 0 && data2 == 0 { 9 return selectData1 == row.data1 10 } 11 if(data1 == 0 && data2 != 0 { 12 return selectData2 == row.data2 13 } 14 return selectData1 == row.data1 && selectData2 == row.data2 15} 16 17return ( 18 <> 19 <select onChange={e => setData1(e.target.value)}}> 20 <option value=0>0<option> 21 <option value=1>1<option> 22 <option value=2>2<option> 23 </select> 24 <select onChange={e => setData2(e.target.value)}}> 25 <option value=0>0<option> 26 <option value=1>1<option> 27 <option value=2>2<option> 28 </select> 29 </> 30)

filterメソッドを使って絞り込みを行っています。
stateはそれぞれvalueを持つように設定されていてselectboxのonChangeによってvalueが入る形になっています。
Datasにはオブジェクトが入ります。
これで絞り込みを実装しているのですが、条件が3個、4個と増えていくとその階乗分if文を書く必要があり、冗長な記述になってしまうため、良い方法がないか考えているところです。

javascript

1 2const [data1, setData1] = useState(0) 3const [data2, setData2] = useState(0) 4const [data3, setData3] = useState(0) 5const selectedDatas = Datas.filter(row => { 6 // 条件がもし増えたら階乗分だけifを書く必要がある。おそらく条件が3個の場合は9個ほど書く必要がある。 7 // if... 8 // if... 9 if(data1 == 0 && data2 == 0 /*&& data3 == 0 */) { 10 return true 11 } 12 if(data1 != 0 && data2 == 0 { 13 return selectData1 == row.data1 14 } 15 if(data1 == 0 && data2 != 0 { 16 return selectData2 == row.data2 17 } 18 return selectData1 == row.data1 && selectData2 == row.data2 19} 20 21return ( 22 <> 23 <select onChange={e => setData1(e.target.value)}}> 24 <option value=0>0<option> 25 <option value=1>1<option> 26 <option value=2>2<option> 27 </select> 28 <select onChange={e => setData2(e.target.value)}}> 29 <option value=0>0<option> 30 <option value=1>1<option> 31 <option value=2>2<option> 32 </select> 33 <select onChange={e => setData3(e.target.value)}}> 34 <option value=0>0<option> 35 <option value=1>1<option> 36 <option value=2>2<option> 37 </select> 38 </> 39)

こちら何か良い方法がありましたら考え方だけでも構いませんのでご教授いただけたらと思います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

微妙に変数名とか変えましたが、こんな感じでしょうか。

js

1import React, { useState } from 'react'; 2 3const fields = [ 4 { key: 'data1', options: [0, 1, 2] }, 5 { key: 'data2', options: [0, 1, 2] }, 6 { key: 'data3', options: [0, 1, 2] }, 7]; 8 9const allData = [ 10 { id: 0, data1: 0, data2: 0, data3: 0 }, 11 { id: 1, data1: 1, data2: 0, data3: 0 }, 12 { id: 2, data1: 2, data2: 0, data3: 0 }, 13 { id: 3, data1: 0, data2: 1, data3: 0 }, 14 { id: 4, data1: 1, data2: 1, data3: 0 }, 15 { id: 5, data1: 2, data2: 1, data3: 0 }, 16 { id: 6, data1: 0, data2: 2, data3: 0 }, 17 { id: 7, data1: 1, data2: 2, data3: 0 }, 18 { id: 8, data1: 2, data2: 2, data3: 0 }, 19 { id: 9, data1: 0, data2: 0, data3: 1 }, 20 { id: 10, data1: 1, data2: 0, data3: 1 }, 21 { id: 11, data1: 2, data2: 0, data3: 1 }, 22 { id: 12, data1: 0, data2: 1, data3: 1 }, 23 { id: 13, data1: 1, data2: 1, data3: 1 }, 24 { id: 14, data1: 2, data2: 1, data3: 1 }, 25 { id: 15, data1: 0, data2: 2, data3: 1 }, 26 { id: 16, data1: 1, data2: 2, data3: 1 }, 27 { id: 17, data1: 2, data2: 2, data3: 1 }, 28 { id: 18, data1: 0, data2: 0, data3: 2 }, 29 { id: 19, data1: 1, data2: 0, data3: 2 }, 30 { id: 20, data1: 2, data2: 0, data3: 2 }, 31 { id: 21, data1: 0, data2: 1, data3: 2 }, 32 { id: 22, data1: 1, data2: 1, data3: 2 }, 33 { id: 23, data1: 2, data2: 1, data3: 2 }, 34 { id: 24, data1: 0, data2: 2, data3: 2 }, 35 { id: 25, data1: 1, data2: 2, data3: 2 }, 36 { id: 26, data1: 2, data2: 2, data3: 2 }, 37]; 38 39const App = () => { 40 const [filter, setFilter] = useState({}); 41 42 const updateFilter = (key, value) => { 43 setFilter({ ...filter, [key]: parseInt(value) }); 44 }; 45 46 const filteredData = allData.filter(row => fields.every(field => 47 !filter[field.key] || row[field.key] == filter[field.key] 48 )); 49 50 return ( 51 <> 52 <div> 53 Filter: 54 {fields.map(field => 55 <select key={field.key} onChange={e => updateFilter(field.key, e.target.value)}> 56 {field.options.map(value => 57 <option key={value} value={value}>{value}</option> 58 )} 59 </select> 60 )} 61 </div> 62 <p> 63 DEBUG: filter = {JSON.stringify(filter)} 64 </p> 65 <ul> 66 {filteredData.map(row => 67 <li key={row.id}>id: {row.id}, {fields.map(field => 68 `${field.key}: ${row[field.key]}` 69 ).join(', ')}</li> 70 )} 71 </ul> 72 </> 73 ); 74}; 75 76export default App;

追記。allData の生成もコードでできて、field が増えても対応可能ですが、ベタ書きの方がわかりやすいですね。

js

1const allData = fields.reduce((acc, field) => acc.flatMap(row => 2 field.options.map(value => ({ ...row, [field.key]: value }) 3)), [{}]).map((row, index) => ({ ...row, id: index }));

投稿2020/12/31 02:15

編集2020/12/31 02:44
hoshi-takanori

総合スコア7899

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hello_space

2020/12/31 02:36

すごく丁寧に回答していただきありがとうございます。まだコードの内容を完全に理解できていないのですがひとまず高評価だけさせていただきました。実装していく中で疑問点がありましたら追記させていただきます。
hoshi-takanori

2020/12/31 02:48

微妙に訂正がありまして、select と option の id を key に修正しました。 コードの内容は非常にわかりにくい (特に追記) と思いますので、遠慮せずにご質問ください。
hello_space

2021/01/04 03:06

休みもあり時間がかかってしまいましたが、無事実装できました!こんな解法があるなんて全く思い付かず、本当に感謝しています。もしよろしければ参考までにこうした解法を思いつくまでの過程やコツのようなものがあれば是非教えていただきたいです。
hello_space

2021/01/04 05:02

連投すみません。コードについてですが、 const filteredData = allData.filter(row => fields.every(field => !filter[field.key] || row[field.key] == filter[field.key] )); に関しては左辺の値が0だとfalseのため!で変換してtrueにして全てのデータを返す、値が0以外ならtrueのためfalseに変換して右辺の処理に移行。右辺ではデータを比べて同じものがあればtrueを返すためeveryの結果がtrueになり表示される。 という理解であっていますでしょうか?
hoshi-takanori

2021/01/04 07:24 編集

> こうした解法を思いつくまでの過程やコツ 難しい質問ですね…。「抽象化」を意識することでしょうか。 「コンピュータサイエンスにおける問題のすべては、もう一段の間接参照によって解決できる」 という格言がありまして、まぁそんな感じです。 filteredData は、 ・filter は実際に選択されたフィルタの状態を表すオブジェクトです。 ・allData.filter は、allData に含まれる各データ (row) に対して、row => fields.every( 〜 ) という関数を呼び出して、結果が true であるものだけを取り出します。 ・fields.every は、各フィールドに対して field => !filter[field.key] || row[field.key] == filter[field.key] という関数を呼び出して、すべてのフィールドに対する結果が true の時だけ true を返します。 ・!filter[field.key] がちょっと曲者で、filter[field.key] が存在しないか、値が 0 の時に true になります。 具体例として、filter の初期値は {} (空オブジェクト) ですが、そこから data1 を 0、data2 を 1 と選択した場合 { data1: 0, data2: 1 } となります。 ここで row が { id: 13, data1: 1, data2: 1, data3: 1 } の場合、 ・field.key == 'data1' に対して、filter[field.key] は 0 なので !filter[field.key] は true ・field.key == 'data2' に対して、filter[field.key] は 1 なので !filter[field.key] は flase だが、row[field.key] == filter[field.key] が成り立つので true ・field.key == 'data3' に対して、filter[field.key] は存在せず undefined となるので !filter[field.key] は true ということで、fields.every は true になり、この row は表示されます。(質問文からフィルターが 0 の場合はすべて表示対象になると判断しましたが、これだと実際のデータが 0 の場合だけ表示するというのができないですね…。) ↑ 読み返したらぜんぜん簡単に説明できてなくてごめんなさい。
hello_space

2021/01/04 10:28

いえいえ、かなり詳細に解説していただきとてもわかりやすかったです。本当にありがとうございます! 実はこのコードは仕事で使用しているコードのため、特定できないように変数名とかもテキトーで実際よりかなりぼやかしながらやりたいことだけをふわっと書いたので自分で質問しておきながらかなりわかりにくい質問であるというのは理解しておりました。 そんななか、これだけ的確な回答ができる理解力とそれを実現できる頭の柔かさに脱帽です! 抽象化、意識してみます!本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問