{"id":15608708,"url":"https://github.com/hypercubed/florida-keys","last_synced_at":"2025-04-28T11:48:24.565Z","repository":{"id":57238653,"uuid":"131673985","full_name":"Hypercubed/florida-keys","owner":"Hypercubed","description":" Pure functional accessor factories in TypeScript.","archived":false,"fork":false,"pushed_at":"2018-05-05T17:24:52.000Z","size":187,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-19T21:55:48.049Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hypercubed.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-01T03:51:07.000Z","updated_at":"2025-03-03T16:21:01.000Z","dependencies_parsed_at":"2022-08-26T15:12:01.807Z","dependency_job_id":null,"html_url":"https://github.com/Hypercubed/florida-keys","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2Fflorida-keys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2Fflorida-keys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2Fflorida-keys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2Fflorida-keys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hypercubed","download_url":"https://codeload.github.com/Hypercubed/florida-keys/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251309900,"owners_count":21568920,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-03T05:21:57.577Z","updated_at":"2025-04-28T11:48:24.532Z","avatar_url":"https://github.com/Hypercubed.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# florida-keys\n\nPure functional accessor factories in TypeScript.\n\n# Install\n\n```bash\nnpm install florida-keys\n```\n\n# Testing\n\n```bash\nnpm install\nnpm test\n```\n\n# Summary of API\n\n```ts\n// import a factory function and accesssory functions from florida-keys\nimport { florida, and } from 'florida-keys';\n\n// This is the type of data we want to access, filter and sort\ninterface Person {\n  first: string;\n  last: string;\n  dob: Date;\n}\n\n// create a florida-keys accessor for a Person type\nconst f = florida\u003cPerson\u003e();\n\n// define some accessor functions\nconst first = f.k('first');\nconst dob = f.k('dob');\nconst fullname = f.g(d =\u003e `${d.last}, ${d.first}`);\n\n// define some conditions\nconst gNames = first.i(0).eq('G');\nconst nineties = and(\n  dob.gte(new Date('1/1/1990')),\n  dob.lt(new Date('1/1/2000'))\n);\n\n// define a sorting function\nconst dobSort = dob.asc();\n\n// The data\nconst people: Person[] = [\n  { first: 'Gil', last: 'Wiggins', dob: new Date('8/1/1992') },\n  ...\n];\n\n// returns an array\npeople\n  .filter(and(gNames, nineties).$) // people with \"G\" names born in the 90's\n  .sort(dobSort.$)                 // sorted by date of birth\n  .map(fullname.$);                // their fullnames\n```\n\n### Accessors\n\n| F                        | Equivalent \n| ------------------------ | ----------------------- |\n| `f.$`                    | `(d: T) =\u003e d`           |\n| `f.k('key').$`           | `(d: T) =\u003e d.key`       |\n| `f.k('key').k('key').$`  | `(d: T) =\u003e d.key.key`   |\n| `f.i(index).$`           | `(d: T) =\u003e d[number]`   |\n| `f.g(fn).$`              | `(d: T) =\u003e fn(d)`       |\n\n_Example_\n\n```ts\nconst f = florida\u003cPerson\u003e();\nconst first = f.k('first');\n\npeople\n  .map(first.$);  // Returns a list of first names\n```\n\n### Operators\n\n| FK               | Equivalent                          |\n| ---------------- | ----------------------------------- |\n| `f.eq(value).$`  | `(d: T): boolean =\u003e d == value` |\n| `f.gt(value).$`  | `(d: T): boolean =\u003e d \u003e  value` |\n| `f.lt(value).$`  | `(d: T): boolean =\u003e d \u003c  value` |\n| `f.gte(value).$` | `(d: T): boolean =\u003e d \u003e= value` |\n| `f.lte(value).$` | `(d: T): boolean =\u003e d \u003c= value` |\n\n_Example_\n\n```ts\nconst f = florida\u003cPerson\u003e();\nconst firstname = f.k('firstname');\nconst gills = firstname.eq('Gil');\n\npeople\n  .filter(gills.$);  // returns a list of Gil's\n```\n\n### Logicals\n\n| FK                         | Equivalent                       |\n| -------------------------- | -------------------------------- |\n| `not(f.eq(a)).$`           | `(d: T) =\u003e !(d === a)`           |\n| `and(f.eq(a), f.lt(b)).$`  | `(d: T) =\u003e (d === a) \u0026\u0026 (d \u003c b)` |\n| `or(f.eq(a), f.lt(b)).$`   | `(d: T) =\u003e (d === a) || (d \u003c b)` |\n\n_Example_\n\n```ts\nconst f = florida\u003cPerson\u003e();\nconst dob = f.k('dob');\nconst nineties = and(\n  dob.gte(new Date('1/1/1990')),\n  dob.lt(new Date('1/1/2000'))\n);\n\npeople\n  .filter(nineties.$);  // returns a list of people born in the nineties\n```\n\n### Sorting\n\n| FK                 | Equivalent\n| ------------------ | ----------------------------------------- |\n| `f.order(fn).$`    | `(a: T, b: T): number =\u003e fn(a, b)`        |\n| `f.asc().$`        | `(a: T, b: T): number =\u003e ascending(a, b)` |\n| `f.desc().$`       | `(a: T, b: T): number =\u003e decending(a, b)` |\n\n_Example_\n\n```ts\nconst f = florida\u003cPerson\u003e();\nconst dobSort = f.k('dob').asc();\n\npeople\n  .sort(dobSort.$);  // returns a list of people sorted by date of birth in ascending order\n```\n\n### Why?\n\nIn TypeScript we often write accessor functions like this:\n\n```ts\nfunction(d: Person) { return d.first; }\n```\n\nor using arrow functions\n\n```ts\n(d: Person) =\u003e d.first\n```\n\nThis simple function returns the value of the `first` key when an object is pass to it.  For example in the `map` function:\n\n```ts\npeople.map((d: Person) =\u003e d.first);\n```\n\nThis is lightweight, simple, and readable.  There is nothing wrong with it.  Sometimes, however, in order to avoid repeating ourselves so we create a reusable accessor function like this:\n\n```ts\nconst firstname = (d: Person) =\u003e d.first;\npeople.map(firstname);\n```\n\nNow imagine the object also has a `dob` key whose values are date objects.  We may want to filter like this:\n\n```ts\nconst firstname = (d: Person) =\u003e d.first;\nconst dob = (d: Person) =\u003e d.dob;\nconst dobFilter = (d: Person) =\u003e d.dob \u003e= new Date('1/1/1990');\n\npeople\n  .filter(dobFilter)\n  .map(firstname);\n```\n\nHowever, this has a couple of drawbacks.  First of all, you will need to create a new filter every time the date changes; also the `Date` constructor is called for every element in the `people` array.  A better approach is an accessor function factory:\n\n```ts\nconst createDobFilter = (date: Date) =\u003e d =\u003e d.dob \u003e= date;\nconst dobFilter = createDobFilter(new Date('1/1/1990'));\n\npeople\n  .filter(dobFilter)\n  .map(firstname);\n```\n\nIt's a little ugly but here the `Date` constructor is only called once and the `createDobFilter` function returns the accessor.  An new accessor can be created any time by calling `createDobFilter`\n\nNow what if we want to filter between two dates.  We can do modify our accessor factory:\n\n```ts\nconst createDobFilter = (a: Date, b: Date) =\u003e d =\u003e d.dob \u003e= a \u0026\u0026 d.dob \u003c b;\n```\n\nbut let's say that you have multidimensional data where dates `a` and `b` change independently.  You might be tempted to do something like this:\n\n```ts\nconst createDobGteFilter = (date: Date) =\u003e d =\u003e d.dob \u003e= date;\nconst createDobLteFilter = (date: Date) =\u003e d =\u003e d.dob \u003c date;\n\nconst dobGteFilter = createDobGteFilter(new Date('1/1/1990'));\nconst dobLteFilter = createDobLteFilter(new Date('1/1/2000'));\n\nconst values = data\n  .filter(dobGteFilter)\n  .filter(dobLteFilter)\n  .map(firstname);\n```\n\nThen let's add sorting by dob and returning the person's full name.\n\n```ts\nconst createDobGteFilter = (date: Date) =\u003e d =\u003e d.year \u003e= date;\nconst createDobLteFilter = (date: Date) =\u003e d =\u003e d.year \u003c date;\n\nconst dob = (d: Person) =\u003e d.dob;\nconst dobGteFilter = createDobGteFilter(new Date('1/1/1990'));\nconst dobLteFilter = createDobLteFilter(new Date('1/1/2000'));\nconst fullname = (d: Person) =\u003e `${d.last}, ${d.first}`;\n\nconst dobSort = (a: Person, b: Person) =\u003e a.dob \u003c b.dob ? -1 : a.dob \u003e b.dob ? 1 : 0;\n\npeople\n  .filter(dobGteFilter)\n  .filter(dobLteFilter)\n  .sort(dobSort)\n  .map(fullname);\n```\n\nOk, no we're getting ridiculous.  The `Date` constructor is not that expensive.  But you can imagine a situation where the values for filters could be very expensive.  For example based on aggregated statistics or reading from the DOM, or, perhaps, we are interating over a lot of data\n\nOk, at this point let me introduce `florida-keys`.  `florida-keys` has simply a set of shortcuts for all this.  For example:\n\n```ts\nconst f = florida\u003cPerson\u003e();\nconst firstname = f.k('first');\npeople.map(firstname.$);\n```\n\nThe value returned from `f.k(key).$` in this case is simply the typed accessor function `function(d: T) { return d[key]; }`.\n\nInteresting.  How about this:\n\n```ts\nconst f = florida\u003cPerson\u003e();\nconst firstname = f.k('first');\nconst dobFilter = f.k('dob').gte(new Date('1/1/1990'));\npeople\n  .filter(dobFilter.$)\n  .map(firstname.$);\n```\n\n`f.k(key).gte(somevalue)` is essentially a shortcut for `function(d: T) { return d['key'] \u003e= somevalue; }`.\n\nIt gets better:\n\n```ts\nconst f = florida\u003cPerson\u003e();\nconst firstname = f.k('first');\nconst dob = f.k('dob');\n\nconst nineties = and(\n  dob.gte(new Date('1/1/1990')),\n  dob.lt(new Date('1/1/2000'))\n);\n\npeople\n  .filter(nineties.$)\n  .map(firstname.$);\n```\n\nor how about this:\n\n```ts\nconst f = florida\u003cPerson\u003e();\nconst firstname = f.k('first');\nconst dob = f.k('dob');\nconst fullname = f.g(d =\u003e `${d.last}, ${d.first}`);\n\nconst gNames = firstname.i(0).eq('G');\nconst nineties = and(\n  dob.gte(new Date('1/1/1990')),\n  dob.lt(new Date('1/1/2000'))\n);\n\nconst filter = and(gNames, nineties);\nconst sort = dob.asc();\n\npeople\n  .filter(filter.$)\n  .sort(sort.$)\n  .map(fullname.$);\n```\n\nPretty neat?\n\n## Acknowledgments\n\nAlso see [utatti/lens.ts](https://github.com/utatti/lens.ts)\n\n## License\nCopyright (c) 2014+ Jayson Harshbarger\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhypercubed%2Fflorida-keys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhypercubed%2Fflorida-keys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhypercubed%2Fflorida-keys/lists"}