📝
条件の数が可変なArray.filter()をやりたかったポエム
動機
ダッシュボードみたいなの作ってて、フィルタリング条件が増えたり減ったりして実装がめんど臭い事が多々あるのであらがってみたかった。MongoのQueryみたいなの妄想したけど、andとかorの条件ネストは諦めた。
そんなに続いてるわけでもないけどコレの続き。
やりたかった事
これに
var data = [
{b: 500, g:{value: 30}},
{b: 200},
{b: 100},
{b: 300},
{string: "aiue", g:{value: 200}},
{string: "aiueo", g:{value: 300}},
]
これを
var conditionParams = [
{type: "match", "key": ["string"], "value": "ueo"},
{type: "lt", "key": ["g", "value"], "value": 400},
]
こうすると
oreoreFilter(data, conditionParams, genCondition, true);
ANDでフィルタリングされてこうなる。
// -> [ {string: "aiueo", g:{value: 300}} ]
ゆるふわ実装
// ネストされたオブジェクトをキーで取り出すやつ
function search(data,keys){
console.log(data, keys);
return keys.reduce((current, key) => {
try{
return current[key]
} catch(e) {
return undefined
}
}, data)
}
// 検索条件を組み立てるやつ
// あとで挙動を変更したり追加できるようにミドルウェアみを持たせとく
function genCondition(condition){
try{
switch(condition.type){
case "eq": return (value)=>{ return search(value, condition.key) === condition.value; }
case "gt": return (value)=>{ return search(value, condition.key) > condition.value; }
case "lt": return (value)=>{ return search(value, condition.key) < condition.value; }
case "ge": return (value)=>{ return search(value, condition.key) >= condition.value; }
case "le": return (value)=>{ return search(value, condition.key) <= condition.value; }
case "match": return (value)=>{ return new RegExp(`.*${condition.value}.*`).test(search(value, condition.key)); }
default: return ()=> false;
}
} catch (e){
return false;
}
}
// フィルタリング処理
// 対象の配列に対して実際にフィルタリングする関数
function oreoreFilter(data, conditionParams, genCondition, flg){
const conditions = conditionParams.map(genCondition)
return data.filter((value)=>{
return conditions.reduce(
(sum, condition)=>{
// 申し訳程度のAND/OR要素
if (flg){
return sum && condition(value)
}
return sum || condition(value)
}
,flg)
})
}
テスト
var data = [
{b: 500, g:{value: 30}},
{b: 200, g:{value: 500}},
{b: 200, g:{value: 300}},
{b: 100},
{b: 300},
{string: "aiue", g:{value: 200}},
{string: "aiueo", g:{value: 500}},
]
var conditionParams = [
{type: "gt", "key": ["b"], "value": 100},
{type: "lt", "key": ["b"], "value": 700},
{type: "lt", "key": ["g", "value"], "value": 400},
]
// AND検索
oreoreFilter(data, conditionParams, genCondition, true);
// -> [ {b: 500, g:{value: 30}}, {b: 200, g:{value: 300}} ]
// OR検索
oreoreFilter(data, conditionParams, genCondition, false);
// -> [
// {b: 500, g:{value: 30}},
// {b: 200, g:{value: 500}},
// {b: 200, g:{value: 300}},
// {b: 100},
// {b: 300},
// {string: "aiue", g:{value: 200}},
// ]
まとめ
とりあえず動いてる気がする。
Discussion
単純な、多重フィルタで関数を指定したほうが、応用が効くような気がしました。
ざくっとした動作コードです。
こちらのコードの方が素直に見通しが良くて良さそうですね。
参考になります!
ネストとかを考慮したいときは木構造のデータ構造をうまく取り入れたら達成しやすいかもです。
定義側
使用側
demo code.