diff options
author | William Carroll <wpcarro@gmail.com> | 2023-01-19T18·09-0800 |
---|---|---|
committer | clbot <clbot@tvl.fyi> | 2023-01-19T18·12+0000 |
commit | 509e356bb8fcba2264368ca1e973e270ab614f98 (patch) | |
tree | 24f141931b5c1708e522701de9907cc5803fd774 | |
parent | 0dfe460fbb8cda0831fbcf4d9e42948c2bb88afa (diff) |
feat(wpcarro/slx.js): Support JavaScript simple-select impl r/5704
See README.md Change-Id: I6a50e34398c42aabe3cceba160be006f1867eca4 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7874 Reviewed-by: wpcarro <wpcarro@gmail.com> Autosubmit: wpcarro <wpcarro@gmail.com> Tested-by: BuildkiteCI
-rw-r--r-- | users/wpcarro/scratch/simple-select/index.html | 9 | ||||
-rw-r--r-- | users/wpcarro/slx.js/README.md | 55 | ||||
-rw-r--r-- | users/wpcarro/slx.js/default.nix | 11 | ||||
-rw-r--r-- | users/wpcarro/slx.js/index.js (renamed from users/wpcarro/scratch/simple-select/index.js) | 52 |
4 files changed, 86 insertions, 41 deletions
diff --git a/users/wpcarro/scratch/simple-select/index.html b/users/wpcarro/scratch/simple-select/index.html deleted file mode 100644 index f7d4576f79a1..000000000000 --- a/users/wpcarro/scratch/simple-select/index.html +++ /dev/null @@ -1,9 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8" /> - </head> - <body> - <script src="./index.js"></script> - </body> -</html> diff --git a/users/wpcarro/slx.js/README.md b/users/wpcarro/slx.js/README.md new file mode 100644 index 000000000000..3fbebc470633 --- /dev/null +++ b/users/wpcarro/slx.js/README.md @@ -0,0 +1,55 @@ +# slx.js + +Filter tabular data in the browser using an ergonomic query language. + +## Status + +This project is usable today (I use it in my projects), but it's currently alpha +status. See the wish list for remaining features. + +## Installation + +`slx.js` is available via CDN: + +```shell +<script src="https://cdn.jsdelivr.net/gh/wpcarro/slx.js/index.js" async></script> +``` + +## Usage + +`slx.js` hasn't been properly benchmarked, but in my personal projects, it works +fine with `O(1,000)s` of records. + +```javascript +const cast = [ + { first: "Graham", last: "Chapman" }, + { first: "John", last: "Cleese" }, + { first: "Terry", last: "Gilliam" }, + { first: "Eric", last: "Idle" }, + { first: "Terry", last: "Jones" }, + { first: "Michael", last: "Palin" }, +]; + +const config = { + // Match values case sensitively when filtering. + caseSensitive: false, + // Coerce values into regular expressions (instead of strings) when they're defined as atoms. + preferRegex: true, + // The key in the JS object that hosts the Date type against which we filter. + dateKey: 'Date', +}; + +console.log(select('last:^C.+$', cast, config)); +// [{ first: "Graham", last: "Chapman" }, { first: "John", last: "Cleese" }] +``` + +## Wish List + +- Support explicit grouping with parentheses (e.g. `title:once (director:Tarantino OR director:Coen)`). +- Proper benchmarking (see "Usage" section). +- Something something documentation. +- Something something testing. + +## See also: + +- [`slx`](https://github.com/wpcarro/slx) diff --git a/users/wpcarro/slx.js/default.nix b/users/wpcarro/slx.js/default.nix new file mode 100644 index 000000000000..bf903e77aadd --- /dev/null +++ b/users/wpcarro/slx.js/default.nix @@ -0,0 +1,11 @@ +{ pkgs, depot, ... }: + +(pkgs.writeText "source.txt" '' + ${depot.third_party.gitignoreSource ./.} +'').overrideAttrs (_: { + meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush { + filter = ":/users/wpcarro/slx.js"; + remote = "git@github.com:wpcarro/slx.js.git"; + ref = "refs/heads/canon"; + }; +}) diff --git a/users/wpcarro/scratch/simple-select/index.js b/users/wpcarro/slx.js/index.js index 15a35ab74334..7eba2dca91bf 100644 --- a/users/wpcarro/scratch/simple-select/index.js +++ b/users/wpcarro/slx.js/index.js @@ -1,21 +1,9 @@ -const state = { - // Match values case sensitively when filtering. - caseSensitive: false, - // Coerce values into regular expressions (instead of strings) when they're defined as atoms. - preferRegex: true, - // The key in the JS object that hosts the Date type against which we filter. - dateKey: 'Date', -}; - -// TODO(wpcarro): Support filtering by date (before, after). -// TODO(wpcarro): Support grouping with parentheses. - -function select(query, xs) { - const predicate = compile(parse(query)); +function select(query, xs, config) { + const predicate = compile(parse(query), config); return xs.filter(predicate); } -function compile(ast) { +function compile(ast, config) { if (ast.type === 'CONJUNCTION') { const lhs = compile(ast.lhs); const rhs = compile(ast.rhs); @@ -38,12 +26,12 @@ function compile(ast) { if (ast.val === 'yesterday') { t.setDate(t.getDate() - 1); console.log(t); - } + } // MM/DD/YYYY else { t = new Date(ast.val); } - return row[state.dateKey] < t; + return row[config.dateKey] < t; }; } if (ast.key === 'after') { @@ -52,12 +40,12 @@ function compile(ast) { if (ast.val === 'yesterday') { t.setDate(t.getDate() - 1); console.log(t); - } + } // MM/DD/YYYY else { t = new Date(ast.val); } - return row[state.dateKey] > t; + return row[config.dateKey] > t; }; } } @@ -71,7 +59,7 @@ function compile(ast) { if (ast.matchType === 'STRING') { return function(row) { return Object.values(row).some(x => { - if (state.caseSensitive) { + if (config.caseSensitive) { return x === ast.val; } else { return x.toLowerCase() === ast.val.toLowerCase(); @@ -87,7 +75,7 @@ function compile(ast) { } if (ast.type === 'STRING') { return function(x) { - if (state.caseSensitive) { + if (config.caseSensitive) { return x === ast.val; } else { return x.toLowerCase() === ast.val.toLowerCase(); @@ -310,18 +298,18 @@ function selection(p) { }; } } else { - return matchAll(p); + return matchAll(p, config); } } -function matchAll(p) { +function matchAll(p, config) { const [type, val] = p.tokens[p.i]; - // Cast atoms into strings or regexes depending on the current state. + // Cast atoms into strings or regexes depending on the current config. if (type === 'ATOM') { p.i += 1; - if (state.preferRegex) { - const regex = state.caseSensitive ? new RegExp(val) : new RegExp(val, "i"); + if (config.preferRegex) { + const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i"); return { type: 'MATCH_ALL', matchType: 'REGEX', val: regex }; } else { return { type: 'MATCH_ALL', matchType: 'STRING', val } @@ -333,20 +321,20 @@ function matchAll(p) { } if (type === 'REGEX') { p.i += 1; - const regex = state.caseSensitive ? new RegExp(val) : new RegExp(val, "i"); + const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i"); return { type: 'MATCH_ALL', matchType: 'REGEX', val: regex }; } throw `Parse Error: Expected a regular expression or a string, but got: ${p.tokens[p.i]}; ${JSON.stringify(p)}`; } -function value(p) { +function value(p, config) { const [type, val] = p.tokens[p.i]; - // Cast atoms into strings or regexes depending on the current state. + // Cast atoms into strings or regexes depending on the current config. if (type === 'ATOM') { p.i += 1; - if (state.preferRegex) { - const regex = state.caseSensitive ? new RegExp(val) : new RegExp(val, "i"); + if (config.preferRegex) { + const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i"); return { type: 'REGEX', val: regex }; } else { return { type: 'STRING', val } @@ -358,7 +346,7 @@ function value(p) { } if (type === 'REGEX') { p.i += 1; - const regex = state.caseSensitive ? new RegExp(val) : new RegExp(val, "i"); + const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i"); return { type, val: regex }; } throw `Parse Error: Expected a regular expression or a string, but got: ${p.tokens[p.i]}; ${JSON.stringify(p)}`; |