Skip to content

Instantly share code, notes, and snippets.

@akingdom
Created August 8, 2025 15:04
Show Gist options
  • Select an option

  • Save akingdom/e7c44549f1e39851bb2bc24d5015bf2f to your computer and use it in GitHub Desktop.

Select an option

Save akingdom/e7c44549f1e39851bb2bc24d5015bf2f to your computer and use it in GitHub Desktop.
High-performance, hybrid property accessor for dot-delimited paths.
// pathGetter.js - MIT License (C) 2025 Andrew Kingdom
/**
* createPathGetter
* ----------------
* Returns a high-performance, hybrid property accessor for dot-delimited paths.
*
* DESIGN:
* - Cold paths (below `threshold` calls) use a manual string parse to avoid `.split()` allocations.
* - Hot paths (≥ `threshold` calls) are promoted to precompiled accessors and cached for speed.
* - Cache is capped (`maxCacheSize`) and evicts oldest hot paths (FIFO) to control memory growth.
*
* PERFORMANCE NOTES:
* - Hot path avoids repeated path parsing entirely.
* - Cold path uses a single-pass index scan instead of `.split()` for minimal GC churn.
* - Uses a single Map for both hit counts and compiled functions to minimize lookups.
* - FIFO eviction via array keeps cache ops simple and fast; replace with LRU if needed.
*
* PARAMETERS:
* @param {number} threshold - Calls before a path is cached (default: 3)
* @param {number} maxCacheSize - Max hot paths to cache (default: 200)
*
* RETURNS:
* @returns {Function} get(obj, path) - Accesses nested property from `obj` via `path`
*
* USAGE:
* const getPath = createPathGetter();
* const obj = { a: { b: 5 } };
* console.log(getPath(obj, 'a.b')); // → 5
*/
function createPathGetter(threshold = 3, maxCacheSize = 200) {
const cache = new Map(); // path -> { count, fn }
const order = []; // FIFO order of hot paths
return function get(obj, path) {
let rec = cache.get(path);
if (rec) {
if (rec.fn) return rec.fn(obj);
if (++rec.count === threshold) {
// Promote to hot fn
const keys = path.split('.'); // could inline manual parse here
const fn = o => {
for (let i = 0; i < keys.length; i++) o = o[keys[i]];
return o;
};
rec.fn = fn;
order.push(path);
if (order.length > maxCacheSize) cache.delete(order.shift());
return fn(obj);
}
} else {
cache.set(path, { count: 1, fn: null });
}
// Cold path: manual parse to avoid split allocations
let val = obj, start = 0, dot;
while ((dot = path.indexOf('.', start)) !== -1) {
val = val[path.slice(start, dot)];
start = dot + 1;
}
return val[path.slice(start)];
};
}
// Example usage
const getPath = createPathGetter();
const obj = { a: { b: 5 } };
console.log(getPath(obj, 'a.b')); // 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment