Filtering Data
filter(globList, options?) is the one method that takes glob
patterns. Properties matched by a glob (explicitly or via *) are kept; anything
matched by a negated ! glob is removed. The result is built from scratch — the
source is not mutated.
Order doesn’t matter; the list is normalized and sorted first.
const car = { brand: 'Ford', model: { name: 'Mustang', year: 1970 } };const n = Notation.create(car);
n.filter(['*', '!model.year']).value; // » { brand: "Ford", model: { name: "Mustang" } }n.filter('model.name').value; // » { model: { name: "Mustang" } }n.filter().value; // » {} (same as filter("") or filter("!*"))car; // » unchangedRestrictive Mode
Section titled “Restrictive Mode”By default an explicitly-listed field survives even if a negated glob also matches it. In restrictive mode, negation wins:
const data = { car: { brand: 'Ford', model: 'Mustang', age: 52 }, user: { name: 'John', age: 40 }};const globs = ['*', '!*.age', 'user.age'];
Notation.create(data).filter(globs).value;// » car: { brand, model }, user: { name, age: 40 } (user.age kept — explicit)
Notation.create(data).filter(globs, { restrictive: true }).value;// » car: { brand, model }, user: { name } (user.age removed)Worked Example: Field Access by Role
Section titled “Worked Example: Field Access by Role”A common use is trimming an API record down to what a given role may see. Take this record:
{ "id": 42, "name": "Jane Doe", "passwordHash": "$2b$10$Q9…", "roles": ["editor"], "profile": { "bio": "Coffee, code, cassettes.", "avatar": "https://cdn.example.com/u/42.png", "social": { "x": "@jane", "github": "jane" } }, "account": { "plan": "pro", "since": "2021-03-09", "billing": { "customerId": "cus_8fK2", "card": { "brand": "visa", "last4": "4242" } } }, "posts": [ { "id": 1, "title": "Hello", "draft": false, "secret": "internal note" }, { "id": 2, "title": "Notation tips", "draft": true, "secret": "wip" } ]}…and a glob list per role:
{ "guest": ["id", "name", "profile.bio", "profile.avatar"], "editor": [ "*", "!passwordHash", "!account.billing", "!posts[*].secret" ], "admin": ["*", "!passwordHash"]}Filter the record with a role’s globs:
import user from './user.json' with { type: 'json' };import fieldGlobs from './field-globs.json' with { type: 'json' };
Notation.create(user).filter(fieldGlobs.editor).value;// » {// id: 42,// name: "Jane Doe",// email: "[email protected]",// roles: ["editor"],// profile: { bio, avatar, social: { x, github } },// account: { plan: "pro", since: "2021-03-09" }, // billing removed// posts: [ { id, title, draft }, { id, title, draft } ] // secret removed// }The editor list (['*', '!passwordHash', '!account.billing', '!posts[*].secret'])
keeps everything, then drops the password hash, the whole billing object, and the
secret of every post via the array wildcard posts[*].secret.
The guest list is the opposite approach — an allow-list of just four fields:
Notation.create(user).filter(fieldGlobs.guest).value;// » { id: 42, name: "Jane Doe", profile: { bio, avatar } }This pattern is fleshed out into a reusable helper in Recipes.