フィルタ条件切替とは何か
フィルタ条件切替は「現在の条件(カテゴリ、価格帯、検索語、ステータスなど)をON/OFFや値変更で切り替え、その都度配列を絞り込む」実務パターンです。ここが重要です:条件を“関数(述語=predicate)”として組み立てて合成(AND/OR)できるようにしておくと、切替がシンプルで拡張に強くなります。配列は filter による非破壊処理が基本です。
単純な条件切替(ON/OFF・値変更の基本形)
真偽フラグと値を同時に扱う
const items = [
{ category: "book", price: 1200, active: true },
{ category: "pen", price: 200, active: false },
{ category: "book", price: 800, active: true }
];
// フィルタ状態(UIの設定を想定)
const filters = {
enabledOnly: true, // ONなら active:true のみ
category: "book", // 空やnullなら無視
minPrice: 500 // undefinedなら無視
};
// 条件を適用
function applyFilters(list, f) {
return list.filter(it =>
(!f.enabledOnly || it.active) && // ONなら適用、OFFなら常に通す
(!f.category || it.category === f.category) && // 値があれば一致を要求
(f.minPrice == null || it.price >= f.minPrice) // null/undefinedなら無視
);
}
const result = applyFilters(items, filters);
JavaScriptここが重要です:ON/OFFは「ONなら条件を適用、OFFなら必ず通す」と書くと切替が直感的になります。値系は == null(null/undefined)で“欠損なら無視”と判定するのが実務に便利です。
述語関数(predicate)を合成する設計
条件を小さな関数に分けてANDで合成
const whereEnabled = f => it => (!f.enabledOnly || it.active);
const whereCategory = f => it => (!f.category || it.category === f.category);
const whereMinPrice = f => it => (f.minPrice == null || it.price >= f.minPrice);
function and(...preds) {
return x => preds.every(p => p(x));
}
function applyFilters(list, filters) {
const pred = and(
whereEnabled(filters),
whereCategory(filters),
whereMinPrice(filters)
);
return list.filter(pred);
}
JavaScriptここが重要です:述語を小分けにすると追加・削除・差し替えが容易になります。合成は every(AND)で決定的・読みやすい。ORが欲しい条件は別途 or(p1, p2) を用意して使い分けます。
AND/OR の切替(柔軟なクエリ)
function or(...preds) {
return x => preds.some(p => p(x));
}
// 例:カテゴリが "book" または "pen" のどちらか
const pred = or(
it => it.category === "book",
it => it.category === "pen"
);
const filtered = items.filter(pred);
JavaScriptここが重要です:UIで“複数選択”を許すと OR が必要になります。ANDとORの境界を仕様で明確化し、述語の合成で表現します。
検索語・正規化・部分一致(実務でよくあるパターン)
入力の正規化を先に行う
const normalize = s =>
s.trim().toLowerCase().replace(/[A-Za-z]/g, ch =>
String.fromCharCode(ch.charCodeAt(0) - 0xFEE0)
);
function whereQuery(f) {
if (!f.q) return () => true; // 無視
const q = normalize(f.q);
return it => normalize(it.name).includes(q);
}
// 例
const filters = { q: " BOOK " };
const pred = whereQuery(filters);
const result = items.filter(pred);
JavaScriptここが重要です:表記ゆれ(全角半角・大文字小文字・前後空白)を正規化してから比較することで、期待どおりの絞り込みになります。正規化は“キー関数”に閉じ込めて再利用できる形にします。
範囲・並び・多段条件の同時適用
数値範囲(min/max)とソートの組み合わせ
function whereRange(f) {
return it =>
(f.min == null || it.price >= f.min) &&
(f.max == null || it.price <= f.max);
}
function apply(list, f) {
const pred = and(whereRange(f), whereQuery(f));
const filtered = list.filter(pred);
return f.sort
? filtered.toSorted((a, b) =>
f.sort === "priceAsc" ? a.price - b.price :
f.sort === "priceDesc" ? b.price - a.price :
a.name.localeCompare(b.name, "ja"))
: filtered;
}
JavaScriptここが重要です:フィルタと並び替えは“別レイヤー”。まず絞り、次にソート。ソートは toSorted の非破壊版を使い、元配列を壊さない。
状態管理(フィルタの更新と適用の分離)
状態を更新する関数と、適用する関数を分ける
function updateFilters(prev, patch) {
return { ...prev, ...patch };
}
function getFiltered(list, filters) {
return applyFilters(list, filters);
}
// 使い方(UIで切替)
let filters = { enabledOnly: false, category: null, minPrice: null };
filters = updateFilters(filters, { enabledOnly: true });
const view = getFiltered(items, filters);
JavaScriptここが重要です:更新(何をON/OFF・値変更するか)と適用(配列へ絞り込み)を分離すると、テスト・再利用が簡単になり、UIロジックが明快になります。更新はイミュータブルで。
パフォーマンス最適化(無駄な再計算を減らす)
キーの前計算(Schwartzian transform)
// 例:重い正規化を何度も計算しない
const prepared = items.map(x => ({ x, key: normalize(x.name) }));
const q = normalize("book");
const filtered = prepared
.filter(p => p.key.includes(q))
.map(p => p.x);
JavaScriptここが重要です:比較関数やフィルタ述語の中で重い処理(正規化・Date化など)を毎回行うと遅い。前計算してから判定すると大幅に速くなることがあります。
デバウンス(入力連打に対する防御)
検索語入力に合わせて毎回フィルタすると重い場合、一定時間入力が止まるまで適用を遅らせます。
function debounce(fn, ms = 200) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
JavaScriptよくある落とし穴と回避策(重要ポイントの深掘り)
“条件がOFFでも弾いてしまう”バグ
enabledOnly が false のときに it.active を誤って参照してしまい、OFFでも絞り込まれる。回避は「ONなら適用、OFFなら常にtrue」のパターンを守る。
null/undefined の扱いが曖昧
値系の条件は == null で“欠損なら無視”。厳密なfalseや0を尊重する場面では ?? で既定値を入れつつ比較する。仕様を決めて一貫させる。
AND/OR の混同
複数選択のカテゴリはOR、複数必須条件はAND。UI要件を明文化し、述語合成で表現する。テストで例示ケースを固定化する。
破壊的変更で元配列を壊す
フィルタは filter(非破壊)。並び替えは toSorted や slice().sort()。元配列を共有していると副作用の温床になる。
すぐ使えるレシピ(現場の定番コード)
基本フィルタ適用
const applyFilters = (list, f) =>
list.filter(it =>
(!f.enabledOnly || it.active) &&
(!f.category || it.category === f.category) &&
(f.minPrice == null || it.price >= f.minPrice) &&
(f.maxPrice == null || it.price <= f.maxPrice)
);
JavaScript述語合成ユーティリティ
const and = (...preds) => x => preds.every(p => p(x));
const or = (...preds) => x => preds.some(p => p(x));
JavaScript複数カテゴリのOR選択
function whereCategories(selected) {
if (!selected?.length) return () => true;
const set = new Set(selected);
return it => set.has(it.category);
}
JavaScript検索語の正規化+部分一致
const normalize = s => s.trim().toLowerCase();
const whereQuery = f => {
if (!f.q) return () => true;
const q = normalize(f.q);
return it => normalize(it.name).includes(q);
};
JavaScriptまとめ
フィルタ条件切替の核心は「条件を述語関数として設計し、ON/OFFや値の有無で“適用するかどうか”を明確に制御する」ことです。AND/ORは合成関数で表現し、検索語は正規化してから部分一致。フィルタとソートは層を分け、非破壊で適用する。状態更新と適用処理を分離し、必要なら前計算やデバウンスで性能を安定させる。これを押さえれば、初心者でも読みやすく拡張しやすいフィルタ切替が、実務に耐える形で書けます。
