uFuzzy Documentation

repository·main·Indexed 25 days ago

https://github.com/leeoniya/ufuzzy

A tiny, efficient fuzzy search library designed to match short search phrases against large lists of strings. Optimized for performance and low memory usage, uFuzzy provides a three-phase process (filter, info, and sort) for efficient list filtering and autocomplete. It supports custom charsets, a universal Unicode mode, out-of-order term matching, substring exclusions, and utilities for highlighting matches in the UI.

Tokens
2.2K
Snippets
4
Records
6
Agent score
34%

What's inside uFuzzy

  1. Install uFuzzy via npm or Browser

    main

    You can install uFuzzy for Node.js environments using npm, or include it directly in the browser via a <script> tag.

    Node.js

    npm i @leeoniya/ufuzzy

    Then require it in your code:

    const uFuzzy = require('@leeoniya/ufuzzy');

    Browser

    Include the IIFE build in your HTML:

    <script src="./dist/uFuzzy.iife.min.js"></script>
  2. Configure Charsets and Alphabets

    main

    uFuzzy is optimized for the Latin/Roman alphabet using non-unicode regular expressions. To support other languages, you can either augment the built-in Latin regexps or use the slower, universal {unicode: true} variant.

    Simple Alphabet Replacement

    Use the alpha option to replace the A-Z and a-z parts of the built-in regexps with your own characters. Case sensitivity is handled automatically.

    // Latin + Norwegian
    let opts = { alpha: "a-zæøå" };
    
    // Latin + Russian
    let opts = { alpha: "a-zа-яё" };

    Unicode / Universal Mode

    For full Unicode support, use the unicode: true option. Note that this is approximately 50%-75% slower than the optimized Latin mode.

    let opts = {
      unicode: true,
      interSplit: "[^\p{L}\d']+",
      intraSplit: "\p{Ll}\p{Lu}",
      intraBound: "\p{L}\d|\d\p{L}|\p{Ll}\p{Lu}",
      intraChars: "[\p{L}\d']",
      intraContr: "'\p{L}{1,2}\b",
    };
    // Latin + Norwegian
    let opts = { alpha: "a-zæøå" };
  3. Configure uFuzzy search options

    main

    uFuzzy provides a wide range of configuration options to fine-tune matching behavior, term boundaries, and result sorting. Use these options to balance between strict literal matching and loose fuzzy matching.

    Term Matching (Intra-term)

    • intraMode: Defines how term matching is performed.
      • 0 (MultiInsert, default): Allows multiple extra characters between term characters.
      • 1 (SingleError): Allows only a single error type within a term.
    • intraIns: Max number of extra characters allowed between each character within a term. Matches the value of intraMode (0 or 1).
    • intraChars: A partial regular expression for allowed insertion characters between characters within a term. Default is [a-z\d'].
    • intraSub, intraTrn, intraDel: For intraMode: 1 only, these determine which error types (Substitution, Transposition, Deletion) to tolerate. 0 for No, 1 for Yes.

    Term Boundaries (Inter-term)

    • interIns: Max number of extra characters allowed between terms. Default is Infinity.
    • interChars: A partial regular expression for allowed characters between terms. Default is . (matches all).
    • interLft: Determines allowable term left boundary.
      • 0: Any boundary (anywhere).
      • 1: Loose (whitespace, punctuation, alpha-num, or case-change transitions).
      • 2: Strict (whitespace or punctuation only).
    • interRgt: Determines allowable term right boundary.
      • 0: Any boundary (anywhere).
      • 1: Loose (whitespace, punctuation, alpha-num, or case-change transitions).
      • 2: Strict (whitespace or punctuation only).

    Filtering and Sorting

    • intraFilt: A callback function for excluding results based on the term, the match, and the index. Signature: (term, match, index) => boolean.
    • sort: A custom result sorting function. Signature: (info, haystack, needle) => idxsOrder. The default sort prioritizes full term matches and character density.
  4. Perform a basic fuzzy search

    main

    To perform a search, follow the three-phase process: filter, info, and sort. This allows you to handle large datasets efficiently by only performing expensive operations on a subset of matches.

    1. Filter: Returns an array of matched indices in original order.
    2. Info: Collects detailed stats (offsets, fuzz level, etc.) and applies prefix/suffix rules. Run this on a reduced subset (e.g., $\le 1,000$ items).
    3. Sort: Determines the final order using the info object.
    let haystack = ['puzzle', 'Super Awesome Thing', 'FileName.js', '/feeding/the/catPic.jpg'];
    let needle = 'feed cat';
    let uf = new uFuzzy({});
    
    // 1. Filter
    let idxs = uf.filter(haystack, needle);
    
    if (idxs != null && idxs.length > 0) {
      // 2. Info (recommended for <= 1,000 items)
      let info = uf.info(idxs, haystack, needle);
    
      // 3. Sort
      let order = uf.sort(info, haystack, needle);
    
      // Render results
      for (let i = 0; i < order.length; i++) {
        console.log(haystack[info.idx[order[i]]]);
      }
    }
    let haystack = [
        'puzzle',
        'Super Awesome Thing (now with stuff!)',
        'FileName.js',
        '/feeding/the/catPic.jpg',
    ];
    
    let needle = 'feed cat';
    
    let opts = {};
    
    let uf = new uFuzzy(opts);
    
    // pre-filter
    let idxs = uf.filter(haystack, needle);
    
    // idxs can be null when the needle is non-searchable (has no alpha-numeric chars)
    if (idxs != null && idxs.length > 0) {
      // sort/rank only when <= 1,000 items
      let infoThresh = 1e3;
    
    if (idxs.length <= infoThresh) {
        let info = uf.info(idxs, haystack, needle);
    
    // order is a double-indirection array (a re-order of the passed-in idxs)
        // this allows corresponding info to be grabbed directly by idx, if needed
        let order = uf.sort(info, haystack, needle);
    
    // render post-filtered & ordered matches
        for (let i = 0; i < order.length; i++) {
          // using info.idx here instead of idxs because uf.info() may have
          // further reduced the initial idxs based on prefix/suffix rules
          console.log(haystack[info.idx[order[i]]]);
        }
      }
      else {
        // render pre-filtered but unordered matches
        for (let i = 0; i < idxs.length; i++) {
          console.log(haystack[idxs[i]]);
        }
      }
    }
  5. Highlight matches in the UI

    main

    uFuzzy provides a highlight utility to wrap matched character ranges in HTML tags or custom DOM elements.

    Basic HTML Highlighting

    Use uFuzzy.highlight(text, ranges) to wrap matches in <mark> tags.

    let innerHTML = '';
    for (let i = 0; i < order.length; i++) {
      let infoIdx = order[i];
      innerHTML += uFuzzy.highlight(
        haystack[info.idx[infoIdx]],
        info.ranges[infoIdx],
      ) + '<br>';
    }

    Custom Marking Function

    You can provide a custom function to define how matches are wrapped (e.g., using <b> tags).

    const mark = (part, matched) => matched ? '<b>' + part + '</b>' : part;
    
    for (let i = 0; i < order.length; i++) {
      let infoIdx = order[i];
      innerHTML += uFuzzy.highlight(
        haystack[info.idx[infoIdx]],
        info.ranges[infoIdx],
        mark,
      ) + '<br>';
    }

    DOM/JSX Element Highlighting

    For complex UI frameworks, you can provide custom mark and append functions to generate actual DOM elements instead of strings.

    const mark = (part, matched) => {
      let el = matched ? document.createElement('mark') : document.createElement('span');
      el.textContent = part;
      return el;
    };
    
    const append = (accum, part) => { accum.push(part); };
    
    for (let i = 0; i < order.length; i++) {
      let infoIdx = order[i];
      let matchEl = document.createElement('div');
    
      let parts = uFuzzy.highlight(
        haystack[info.idx[infoIdx]],
        info.ranges[infoIdx],
        mark,
        [],
        append,
      );
    
      matchEl.append(...parts);
      domElems.push(matchEl);
    }
    let innerHTML = '';
    
    for (let i = 0; i < order.length; i++) {
      let infoIdx = order[i];
    
    innerHTML += uFuzzy.highlight(
        haystack[info.idx[infoIdx]],
        info.ranges[infoIdx],
      ) + '<br>';
    }
    
    console.log(innerHTML);
  6. Use Integrated Search for out-of-order terms and exclusions

    main

    The uf.search() method is a high-level wrapper that combines filter, info, and sort into a single call. It is optimized for matching terms out of order and supports substring exclusions (e.g., fruit -green -melon).

    Signature: uf.search(haystack, needle, outOfOrder = 0, infoThresh = 1e3) => [idxs, info, order]