Skip to content

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; // » unchanged

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)

A common use is trimming an API record down to what a given role may see. Take this record:

cases/api/user.json
{
"id": 42,
"name": "Jane Doe",
"email": "[email protected]",
"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:

cases/api/field-globs.json
{
"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.