{"id":19639580,"url":"https://github.com/caesarsol/traph","last_synced_at":"2025-04-28T10:31:14.895Z","repository":{"id":57379263,"uuid":"81002648","full_name":"caesarsol/traph","owner":"caesarsol","description":"Transformation graph for JS Objects, a powerful and declarative way to remap one Object in another.","archived":false,"fork":false,"pushed_at":"2020-05-01T14:34:25.000Z","size":61,"stargazers_count":38,"open_issues_count":5,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-05T08:23:11.743Z","etag":null,"topics":["functional-js","js","lazy","object","transformations"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/caesarsol.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-05T15:45:01.000Z","updated_at":"2023-03-23T16:50:06.000Z","dependencies_parsed_at":"2022-09-02T17:34:35.661Z","dependency_job_id":null,"html_url":"https://github.com/caesarsol/traph","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caesarsol%2Ftraph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caesarsol%2Ftraph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caesarsol%2Ftraph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caesarsol%2Ftraph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/caesarsol","download_url":"https://codeload.github.com/caesarsol/traph/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251295296,"owners_count":21566433,"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":["functional-js","js","lazy","object","transformations"],"created_at":"2024-11-11T13:02:18.765Z","updated_at":"2025-04-28T10:31:14.882Z","avatar_url":"https://github.com/caesarsol.png","language":"JavaScript","readme":"# traph\n\nThis is **traph** (**tr**ansformation gr**aph**), a JavaScript mini-library to declaratively transform an Object in another using pure transformation functions.\n\nInspired from the wonderful [`graph`](http://plumatic.github.io/prismatics-graph-at-strange-loop) utility of the [`plumbing`](https://github.com/plumatic/plumbing) clojure library.\n\n## Usage\n\nWe will examine an ordinary transformation function and then its `traph` equivalent.\n\nHere's an old-style function definition:\n\n```js\nfunction stats (input) {\n  const { values } = input\n  if (!values) throw new Error('No property \"values\"!')\n\n  const count = values.length\n  const mean = values.reduce(sum) / count\n  const meanSquare = values.map(square).reduce(sum) / count\n  const variance = meanSquare - square(mean)\n  const output = {\n    count,\n    mean,\n    meanSquare,\n    variance,\n  }\n  return output\n}\n\nfunction sum (a, b) { return a + b }\nfunction square (a) { return a * a }\n\nconst data = { values: [1,2,3,4,5,6,7] }\nconst transformed = stats(data)\nconsole.log(transformed)\n// -\u003e Object {count: 7, mean: 4, meanSquare: 20, variance: 4}\n```\n\nThese computations are hiding a data graph:\n\n```\n{ values } --\u003e { count, mean, meanSquare, variance }\n\n   .-------------------------.\n   .                         v\nvalues --------\u003e count ----\u003e mean -------.\n   '              '--------\u003e meanSquare -'--\u003e variance\n   '-------------------------^\n```\n\nThe dependencies are expressed as the order of the variables,\nan approach which has its liabilities: if you simply exchange the definitions of `variance` and `meanSquare` the function explodes.\n\nUsing `traph`, we can refactor `stats` to this very readable, robust and structured form, using a map from keywords to keyword functions:\n\n```js\nimport traph from 'traph'\n\nconst stats = traph({\n  count:      (i, o) =\u003e i.values.length,\n  variance:   (i, o) =\u003e o.meanSquare - square(o.mean),\n  mean:       (i, o) =\u003e i.values.reduce(sum) / o.count,\n  meanSquare: (i, o) =\u003e i.values.map(square).reduce(sum) / o.count,\n})\n\nconst data = { values: [1,2,3,4,5,6,7] }\nconst transformed = stats(data)\nconsole.log(transformed)\n// -\u003e Object {count: 7, meanSquare: 20, mean: 4, variance: 4}\n```\n\nEvery output value is expressed with an arrow function, where the `i` and `o` parameters represent *input* (`data`) and *output* (the future returned object) respectively.\n\nWe can express output values in function of other output values, `traph` will understand the underlying graph and execute the statements in the right order!\n\nMoreover, each micro-function in the object values is executed exacly once, and the result cached for next calls. (You can verify this by logging from inside the arrow functions)\n\nThis is why the given functions need to be **pure**.\n\n## Validation\n\nA `traph` automatically validates input data, and throws if a property on `input` is non-existant.\n\n## Laziness\n\nSometimes we define a heavy computation, rich of all the data we might need, but somewhere we need only a subset of these.\n\nFor these cases we have `.lazy`.\n\nFor example, suppose we only need the `mean` of our data:\n\n```js\nconst data = { values: [1,2,3,4,5,6,7] }\nconst transformed = stats.lazy(data)\n\n// The object is initially empty:\nconsole.log(transformed)\n// -\u003e Object {}\n\n// We materialize computations as they are needed:\nconsole.log(transformed.mean)\n// -\u003e 4\n\n// The results are then attached to the object,\n// but anything we don't need is not computed:\nconsole.log(transformed)\n// -\u003e Object {count: 7, mean: 4}\n```\n\nThis is possible thanks to a clever trick using prototype getters.\n\nWe don't recommend using lazy objects to feed data to other libraries, since an enumeration of the lazy object keys would give different results depending on the technique used.\n\nThe trick is mainly useful (and has no risks) if you get the result inline:\n\n```\nconst mean = stats.lazy(data).mean\n```\n\nThis guarantees to execute the minimum of necessary steps to get the result.\n\n## TODO\n\n + Validation of outputs, when called fron inside the prop function\n + Better validation of inputs, if needed, maybe using JSON schemas\n + Get near the extensibility and composability of `plumbing/graph`:\n\n   \u003e We can also have higher-order functions on Graphs to wrap the behavior on each step.\n   \u003e For instance, we can automatically profile each sub-function in 'stats' to see how long it takes to execute.\n\n## License\n\nThis software is distributed under the MIT license.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaesarsol%2Ftraph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaesarsol%2Ftraph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaesarsol%2Ftraph/lists"}