Skip to Content
Grab N GoSearch

Search

Syntax

includes
const items = ['apple', 'banana', 'cherry'] items.includes('banana') // true items.includes('grape') // false 'hello world'.includes('world') // true
indexOf
const items = ['a', 'b', 'c', 'b'] items.indexOf('b') // 1 — first match items.indexOf('z') // -1 — not found items.lastIndexOf('b') // 3 — last match
find & findIndex
const users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, ] users.find((u) => u.id === 2) // { id: 2, name: 'Bob' } users.findIndex((u) => u.id === 2) // 1 users.find((u) => u.id === 9) // undefined
some & every
const nums = [1, 2, 3, 4, 5] nums.some((n) => n > 4) // true — at least one nums.every((n) => n > 0) // true — all pass nums.every((n) => n > 3) // false
String Search
const str = 'The quick brown fox' str.startsWith('The') // true str.endsWith('fox') // true str.search(/quick/i) // 4 — index of match str.match(/brown/) // ['brown']
filter (search subset)
const words = ['apple', 'avocado', 'banana', 'apricot'] // find all that start with 'a' words.filter((w) => w.startsWith('a')) // ['apple', 'avocado', 'apricot'] // filter IS search when you want all matches

Beginner Patterns

Case-Insensitive Search
function search(items: string[], query: string) { const q = query.toLowerCase() return items.filter((item) => item.toLowerCase().includes(q)) } search(['Apple', 'BANANA', 'cherry'], 'an') // ['BANANA']
Search Object Array
interface Product { name: string category: string } function searchProducts(products: Product[], query: string) { const q = query.toLowerCase() return products.filter( (p) => p.name.toLowerCase().includes(q) || p.category.toLowerCase().includes(q) ) }
Regex Search
function regexSearch(items: string[], pattern: string) { try { const re = new RegExp(pattern, 'i') return items.filter((item) => re.test(item)) } catch { return [] // invalid regex } } regexSearch(['Apple', 'Banana', 'Apricot'], '^a') // ['Apple', 'Apricot']
Find or Throw
function getUser(users: User[], id: string): User { const user = users.find((u) => u.id === id) if (!user) throw new Error(`User ${id} not found`) return user } // useful when you know the item must exist
Has Duplicate
function hasDuplicate<T>(items: T[]): boolean { return new Set(items).size !== items.length } hasDuplicate([1, 2, 3]) // false hasDuplicate([1, 2, 2]) // true
Match Positions
function findMatches(text: string, query: string) { const matches: number[] = [] const lower = text.toLowerCase() const q = query.toLowerCase() let i = lower.indexOf(q) while (i !== -1) { matches.push(i) i = lower.indexOf(q, i + 1) } return matches } findMatches('banana', 'an') // [1, 3]

Intermediate Patterns

Multi-Field Search
function search<T extends Record<string, unknown>>( items: T[], query: string, fields: (keyof T)[] ) { const q = query.toLowerCase() return items.filter((item) => fields.some((f) => String(item[f]).toLowerCase().includes(q)) ) } search(users, 'alice', ['name', 'email'])
Debounce Function
function debounce<T extends (...args: any[]) => void>( fn: T, ms: number ) { let id: ReturnType<typeof setTimeout> return ((...args: Parameters<T>) => { clearTimeout(id) id = setTimeout(() => fn(...args), ms) }) as T } const search = debounce((q: string) => { console.log('searching:', q) }, 300)
Search with Map Index
// pre-build a lookup map for O(1) access const userMap = new Map(users.map((u) => [u.id, u])) function getUser(id: string) { return userMap.get(id) // instant — no array scan } // vs find which is O(n) every call users.find((u) => u.id === id)
Fuzzy Match (simple)
function fuzzy(str: string, query: string): boolean { let j = 0 const s = str.toLowerCase() const q = query.toLowerCase() for (let i = 0; i < s.length && j < q.length; i++) { if (s[i] === q[j]) j++ } return j === q.length } fuzzy('JavaScript', 'jvs') // true — J..v..S matched in order fuzzy('JavaScript', 'xyz') // false
Binary Search (sorted)
function binarySearch(sorted: number[], target: number): number { let lo = 0 let hi = sorted.length - 1 while (lo <= hi) { const mid = (lo + hi) >>> 1 if (sorted[mid] === target) return mid if (sorted[mid] < target) lo = mid + 1 else hi = mid - 1 } return -1 // not found } // O(log n) — only works on sorted arrays
URL Search Params
function searchFromURL(url: string, items: string[]) { const { searchParams } = new URL(url) const q = searchParams.get('q')?.toLowerCase() if (!q) return items return items.filter((item) => item.toLowerCase().includes(q) ) } // https://example.com?q=phone → filters by "phone" searchFromURL(location.href, products)

Advanced Patterns

Trie (prefix search)
class Trie { children = new Map<string, Trie>() isEnd = false insert(word: string) { let node: Trie = this for (const ch of word) { if (!node.children.has(ch)) node.children.set(ch, new Trie()) node = node.children.get(ch)! } node.isEnd = true } search(prefix: string): boolean { let node: Trie = this for (const ch of prefix) { if (!node.children.has(ch)) return false node = node.children.get(ch)! } return true // prefix exists } }
Inverted Index
// build a word → items index for fast full-text search function buildIndex(items: { id: string; text: string }[]) { const index = new Map<string, Set<string>>() for (const item of items) { for (const word of item.text.toLowerCase().split(/\W+/)) { if (!index.has(word)) index.set(word, new Set()) index.get(word)!.add(item.id) } } return index } // lookup is O(1) per word const ids = index.get('react') // Set of matching item IDs
Ranked Results
function rankedSearch(items: Product[], query: string) { const q = query.toLowerCase() return items .map((item) => { const name = item.name.toLowerCase() let score = 0 if (name === q) score = 3 // exact match else if (name.startsWith(q)) score = 2 // prefix match else if (name.includes(q)) score = 1 // contains return { item, score } }) .filter((r) => r.score > 0) .sort((a, b) => b.score - a.score) .map((r) => r.item) }
Search with Web Worker
// worker.ts — offload search to a background thread self.onmessage = (e: MessageEvent<{ items: string[]; query: string }>) => { const { items, query } = e.data const q = query.toLowerCase() const results = items.filter((i) => i.toLowerCase().includes(q)) self.postMessage(results) } // main.ts const worker = new Worker(new URL('./worker.ts', import.meta.url)) worker.postMessage({ items: allItems, query: 'phone' }) worker.onmessage = (e) => console.log(e.data) // keeps main thread free for large datasets
Fetch with Abort
// cancel stale requests when a new search fires let controller: AbortController | null = null async function searchAPI(query: string): Promise<Item[]> { controller?.abort() // cancel previous request controller = new AbortController() const res = await fetch(`/api/search?q=${query}`, { signal: controller.signal, }) return res.json() } // each call aborts the previous — only latest resolves
Last updated on