実務向け filter テンプレート
// 1) 基本(真偽で抽出)
const over10 = arr.filter(x => x > 10);
// 2) 型・null防御
const validUsers = (arr ?? []).filter(u =>
u && typeof u.id === 'string' && typeof u.name === 'string'
);
// 3) 複数条件(AND / OR / NOT)
const premiumAdults = users.filter(u => u.age >= 18 && u.plan === 'premium');
const hasEmailOrPhone = users.filter(u => !!u.email || !!u.phone);
const excludeInactive = users.filter(u => !u.inactive);
// 4) 部分一致・大文字小文字無視
const q = 'tokyo';
const matched = items.filter(it =>
(it.city ?? '').toLowerCase().includes(q.toLowerCase())
);
// 5) ユニーク抽出(Setを併用)
const uniqueIds = [...new Set(users.map(u => u.id))];
// 6) filter → map の王道連携
const adultIds = users.filter(u => u.age >= 18).map(u => u.id);
// 7) truthy だけ残す(空文字・null/undefined を除外)
const cleaned = values.filter(Boolean);
JavaScriptfilter は「条件に合う要素だけを新配列として返す」非破壊的メソッドで、コールバック引数は element, index, array の3つ、疎配列の空スロットは呼ばれません。
性能とセキュリティのベストプラクティス
- filter の目的を守る: 変換は map、集約は reduce、副作用は forEach。filter は「選別」に専念させると読みやすく保守しやすい。
- 条件式は関数に分離: 複雑な条件は純粋関数に切り出し、テスト可能にする。再利用で重複を削減。
- 巨大入力は段階化: 数十万件規模はチャンク処理・ストリーミング・仮想リストで分割。UI 側では v-virtual-list 等を検討。
- 非同期誤用を避ける: filter のコールバックは同期で真偽を返す前提。非同期条件が必要なら先にメタ情報を取得してから同期 filter、または逐次 for…of+await。
- 破壊的操作禁止: filter 中に元配列へ splice/push しない。選別結果は新配列で扱う。
- 入力検証・エスケープ: 外部入力の配列はスキーマ検証。文字列を UI/DB/HTML に渡す場合はエスケープ・パラメータ化でインジェクション対策。
- 疎配列の挙動: 空スロットはスキップされる点を理解して条件設計する。
React の実務テンプレート
// 検索+表示(安定 key)
function UserList({ users, query }) {
const q = (query ?? '').trim().toLowerCase();
const view = (users ?? [])
.filter(u => u && typeof u.id === 'string' && typeof u.name === 'string')
.filter(u => u.name.toLowerCase().includes(q)) // 条件抽出
.map(u => ({ id: u.id, name: u.name.trim() })); // 整形は map
return (
<ul>
{view.map(u => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
JSXReact では「抽出は filter、整形は map、描画は宣言的に」という使い分けが基本です。for/forEach を JSX 内に直接書かず、前処理と表示を分離します Zenn Qiita。
Vue の実務テンプレート
<template>
<ul>
<li v-for="u in filtered" :key="u.id">
{{ u.name }} — {{ u.role ?? 'N/A' }}
</li>
</ul>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
users: { type: Array, required: true },
query: { type: String, default: '' }
});
const filtered = computed(() => {
const q = (props.query ?? '').trim().toLowerCase();
return (props.users ?? [])
.filter(u => u && typeof u.id === 'string' && typeof u.name === 'string')
.filter(u => u.name.toLowerCase().includes(q))
.map(u => ({ id: u.id, name: u.name.trim(), role: u.role ?? 'N/A' }));
});
</script>
HTMLfilter は「条件抽出」、map は「整形」という役割分担が分かりやすく、computed によるメモ化で不要再計算も抑制できます。
Node.js の実務テンプレート
// ログ行の抽出(レベルと期間でフィルタ)
export function selectLogs(lines, { levels = ['error', 'warn'], since }) {
const allow = new Set(levels);
const ts = since ? new Date(since).getTime() : 0;
return (lines ?? [])
.filter(l => l && typeof l.level === 'string' && typeof l.time === 'string')
.filter(l => allow.has(l.level.toLowerCase()))
.filter(l => new Date(l.time).getTime() >= ts);
}
// 抽出後に I/O(逐次/並列は要件で選択)
export async function saveFiltered(db, rows) {
for (const r of rows) {
await db.insert(r);
}
}
JavaScriptfilter は同期判定に向くため、I/O は抽出後に分離します。並列 I/O が必要なら別途並列数制御を導入します Zenn。
ステップバイステップ具体実装(注文配列から条件抽出→表示→保存)
1. 型と検証
type Order = { id: string; item: string; qty: number; price: number; status?: 'open'|'closed'|'cancel' };
export function isOrder(x: any): x is Order {
return x
&& typeof x.id === 'string'
&& typeof x.item === 'string'
&& Number.isFinite(x.qty)
&& Number.isFinite(x.price);
}
TypeScript2. 条件抽出(filter)
export function selectExpensiveOpenOrders(orders, min = 20000) {
return (Array.isArray(orders) ? orders : [])
.filter(isOrder)
.filter(o => o.status === 'open')
.filter(o => Math.max(0, o.qty) * Math.max(0, o.price) >= min);
}
JavaScript3. 整形(map)
export function toRows(orders) {
return orders.map(o => {
const qty = Math.max(0, o.qty);
const price = Math.max(0, o.price);
const total = qty * price;
return {
id: o.id,
label: `${o.item} x${qty}`,
total,
expensive: total >= 20000,
};
});
}
JavaScript4. React で表示
function OrderList({ orders }) {
const selected = toRows(selectExpensiveOpenOrders(orders));
return (
<section>
{selected.map(r => (
<article key={r.id} className={r.expensive ? 'highlight' : ''}>
<header>{r.label}</header>
<div>¥{r.total.toLocaleString()}</div>
</article>
))}
</section>
);
}
JSX5. Vue で表示
<template>
<section>
<article v-for="r in rows" :key="r.id" :class="{ highlight: r.expensive }">
<header>{{ r.label }}</header>
<div>¥{{ r.total.toLocaleString() }}</div>
</article>
</section>
</template>
<script setup>
import { computed } from 'vue';
import { selectExpensiveOpenOrders, toRows } from './orders.js';
const props = defineProps({ orders: { type: Array, required: true } });
const rows = computed(() => toRows(selectExpensiveOpenOrders(props.orders)));
</script>
HTML6. Node.js で保存(逐次)
import { selectExpensiveOpenOrders, toRows } from './orders.js';
export async function saveFilteredOrders(db, orders) {
const rows = toRows(selectExpensiveOpenOrders(orders));
for (const r of rows) {
await db.insert({ id: r.id, label: r.label, total: r.total });
}
}
JavaScript小さなライブラリ推奨とミニユーティリティ
- 役割の徹底: filter で絞り、map で整形、必要なら reduce で集約。学習・現場ノウハウでもこの使い分けが基本として説明されています。
- 部分導入: lodash-es のピンポイント import(例:
import uniq from 'lodash-es/uniq')でバンドル軽量化。 - 再利用ヘルパ: よく使う抽出や整形を小さな関数に抽出。
// compactFilter: truthy のみ残す(型ナローイング向け)
export function compactFilter(arr) {
return (arr ?? []).filter(Boolean);
}
// byRange: 数値範囲抽出
export function byRange(arr, min, max) {
return (arr ?? []).filter(n => Number.isFinite(n) && n >= min && n <= max);
}
// includeText: 部分一致抽出(大小無視)
export function includeText(arr, key, query) {
const q = (query ?? '').toLowerCase();
return (arr ?? []).filter(x => String(x?.[key] ?? '').toLowerCase().includes(q));
}
JavaScript補足: filter は「条件に合致した要素だけの新配列」を返す非破壊的メソッド。map や forEach との使い分けは多数の解説で整理されており、filter→map の連携も定番です。
