{"id":18675017,"url":"https://github.com/fsvieira/cset","last_synced_at":"2025-11-07T04:30:43.823Z","repository":{"id":34170631,"uuid":"170027707","full_name":"fsvieira/cset","owner":"fsvieira","description":"Combinatorial/Cartesian Lazzy Set ","archived":false,"fork":false,"pushed_at":"2023-03-02T05:11:49.000Z","size":1228,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-23T09:13:59.491Z","etag":null,"topics":["cartesian","combinatorial","difference","intersection","lazzy","product","sets","subset","union"],"latest_commit_sha":null,"homepage":null,"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/fsvieira.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-10T21:10:49.000Z","updated_at":"2022-03-16T17:08:35.000Z","dependencies_parsed_at":"2024-11-07T09:23:55.691Z","dependency_job_id":"488db57d-ef6f-455e-a112-bc9225aa9cb8","html_url":"https://github.com/fsvieira/cset","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fsvieira%2Fcset","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fsvieira%2Fcset/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fsvieira%2Fcset/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fsvieira%2Fcset/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fsvieira","download_url":"https://codeload.github.com/fsvieira/cset/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239520085,"owners_count":19652623,"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":["cartesian","combinatorial","difference","intersection","lazzy","product","sets","subset","union"],"created_at":"2024-11-07T09:21:52.031Z","updated_at":"2025-02-18T17:41:06.204Z","avatar_url":"https://github.com/fsvieira.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CSet\n\nCSet is a JavaScript lazy Set library with support for cartesian product and predicate filtering, that is loosely based on \ntuple relation calculus and relation algebra.\n\nCurrently CSet support most normal set operation, including cartesian product.\n\n# Changes\n\n## 3.0.0\n  * Version schema is now based on Semantic Version System (https://semver.org/)\n  * From now to the future CSet will only support positive integers as elements.\n  * Select now supports partial filtering.\n\n# Install\n\n```\n    npm install cset\n```\n\n# Use (API)\n\n\n## CSetArray\n\nCreate a set, from Array of values.\nAfter set creation all operations on set are chainable, and they are not destructive and a new set is returned.\n\n\n```javascript\n    const {CSetArray} = require(\"cset\");\n\n    const A = new CSetArray([1, 2, 3]);\n```\n\n## Intersection\n\nCreates a set with the intersection of two sets.\n\n```javascript\n    const A = new CSetArray([1, 2, 3]).intersect(\n        new CSetArray([1, 2])\n    );\n```\n\n### Intersection of cartesian/cross product\n\nBoth cartesian/cross product must have same headers, headers don't need to be in the same order.\n\n```javascript\n    const a = new CSetArray([1, 2]).as(\"A\");\n    const b = new CSetArray([3, 4]).as(\"B\");\n\n    const c = new CSetArray([1, 2]).as(\"A\");\n    const d = new CSetArray([3, 5]).as(\"B\");\n\n    const ab = a.crossProduct(b);\n    const dc = d.crossProduct(c);\n\n    console.log(dc.header); // [\"B\", \"A\"];\n    console.log(ab.header); // [\"A\", \"B\"];\n\n    const ab_INTERSECT_dc = ab.intersect(dc);\n\n    consol.log([...ab_INTERSECT_dc.values()]); // [[1,3],[2,3]]\n```\n\n\n## Union\n\nCreates a set with the union of two sets.\n\n```javascript\n    const A = new CSetArray([1, 2, 3]).union(\n        new CSetArray([1, 2])\n    );\n```\n\n### Union of cartesian/cross product\n\nBoth cartesian/cross product must have same headers, headers don't need to be in the same order.\n\n```javascript\n    const a = new CSetArray([1, 2]).as(\"A\");\n    const b = new CSetArray([3, 4]).as(\"B\");\n\n    const c = new CSetArray([5, 6]).as(\"A\");\n    const d = new CSetArray([7, 8]).as(\"B\");\n\n    const ab = a.crossProduct(b);\n    const cd = c.crossProduct(d);\n\n    const ab_UNION_cd = ab.union(cd);\n\n    console.log([...ab_UNION_cd.values()]); // [[1,3],[1,4],[2,3],[2,4],[5,7],[6,7],[5,8],[6,8]]\n\n```\n\n## Difference\n\nCreates a set with the difference of two sets.\n\n```javascript\n    const A = new CSetArray([1, 2, 3]).difference(\n        new CSetArray([1, 2])\n    );\n```\n\n### Difference of cartesian/cross product\n\nBoth cartesian/cross product must have same headers, headers don't need to be in the same order.\n\n```javascript\n    const a = new CSetArray([1, 2]).as(\"A\");\n    const b = new CSetArray([3, 4]).as(\"B\");\n\n    const c = new CSetArray([1, 2]).as(\"A\");\n    const d = new CSetArray([3, 5]).as(\"B\");\n\n    const ab = a.crossProduct(b);\n    const dc = d.crossProduct(c);\n\n    const ab_DIFFERENCE_dc = ab.difference(dc);\n\n    console.log([...ab_DIFFERENCE_dc.values()]); // [[1,4],[2,4]]\n```\n\n\n## SymmetricDifference\n\nCreates a set with the symmetric difference of two sets.\n\n```javascript\n    const A = new CSetArray([1, 2, 3]).symmetricDifference(\n        new CSetArray([1, 2])\n    );\n```\n\n### SymmetricDifference of cartesian/cross product\n\nBoth cartesian/cross product must have same headers, headers don't need to be in the same order.\n\n```javascript\n    const a = new CSetArray([1, 2]).as(\"A\");\n    const b = new CSetArray([3, 4]).as(\"B\");\n\n    const c = new CSetArray([1, 2]).as(\"A\");\n    const d = new CSetArray([3, 5]).as(\"B\");\n\n    const ab = a.crossProduct(b);\n    const dc = d.crossProduct(c);\n\n    const ab_SYMMETRIC_DIFFERENCE_dc = ab.symmetricDifference(dc);\n\n    console.log(JSON.stringify([...ab_SYMMETRIC_DIFFERENCE_dc.values()])); // [[1,4],[2,4],[1,5],[2,5]]\n```\n\n## Cartesian/Cross Product\n\nCreates a set with the cartesian/cross product of two sets.\n\n```javascript\n    const A = new CSetArray([1, 2, 3]).crossProduct(\n        new CSetArray([1, 2])\n    );\n```\n\n## Has\n\nIt checks if an element is in the provided set.\n\n```javascript\n    const A = new CSetArray([1, 2, 3]);\n    \n    A.has(1); // True\n    A.has(4); // False\n```\n\n## Values\n\nIterates all values of a set.\n\n```javascript\n    const A = new CSetArray([1, 2, 3]);\n\n    for (let e of A.values()) {\n        console.log(e); // will print all elements on A.\n    }\n```\n\n## isEmpty\n\nChecks if set is empty.\n\n```javascript\n  const empty = new CSetArray([]);\n  const intersectEmpty = new CSetArray([1, 2]).intersect(new CSetArray([3, 4]));\n  const notEmpty = new CSetArray([1, 2]).intersect(new CSetArray([2, 3, 4]));\n\n  console.log(empty.isEmpty()); // True\n  console.log(intersectEmpty.isEmpty()); // True\n  console.log(notEmpty.isEmpty()); // False\n```\n## isSubset\n\nCheck if set is a subset of other set.\n\n```javascript\n  const a = new CSetArray([0, 1, 2]);\n  const b = new CSetArray([0, 1, 2, 3, 4, 5]);\n  \n  console.log(a.isSubset(b)); // True \n  console.log(b.isSubset(b)); // True\n  console.log(b.isSubset(a)); // False\n```\n\n## isProperSubset\n\nCheck if set is a proper subset of other set.\n\n```javascript\n  const a = new CSetArray([0, 1, 2]);\n  const b = new CSetArray([0, 1, 2, 3, 4, 5]);\n  \n  console.log(a.isProperSubset(a)); // False, all elements of a are in a, so its not proper subset.\n  console.log(a.isProperSubset(b)); // True, all lements of a are in b, \n```\n\n## isSuperset\n\nCheck if set is a superset of other set.\n\n```javascript\n  const a = new CSetArray([0, 1, 2]);\n  const b = new CSetArray([0, 1, 2, 3, 4, 5]);\n  \n  console.log(a.isSuperset(b)); // False\n  console.log(b.isSuperset(b)); // True\n  console.log(b.isSuperset(a)); // True\n\n```\n\n## isProperSuperset\n\nCheck if set is a proper superset of other set.\n\n```javascript\n  const a = new CSetArray([0, 1, 2]);\n  const b = new CSetArray([0, 1, 2, 3, 4, 5]);\n  \n  console.log(a.isProperSuperset(a)); // False \n  console.log(a.isProperSuperset(b)); // False\n  console.log(b.isProperSuperset(a)); // True\n```\n\n## isEqual\n\nCheck if two sets are equal.\n\n```javascript\n  const a = new CSetArray([0, 1, 2]);\n  const b = new CSetArray([0, 1, 2, 3, 4, 5]);\n  \n  console.log(a.intersect(b).isEqual(a)); // True\n  console.log(a.isEqual(b)); // False\n  console.log(a.isEqual(a)); // True\n  console.log(b.isEqual(a)); // False\n```\n\n## As\n\nIt binds an alias to a set. The \"as\" operation is normally useful to use with select.\n\n```javascript\n    const A = new CSetArray([1, 2, 3]).as(\"A\");\n    const B = A.as(\"B\");\n\n    // A and B are same sets with different alias.\n    const C = new CSetArray([4, 5]);\n    const AC = A.union(C).as(\"AC\"); // add alias to A and C union. \n```\n\n### Alias on cartesian/cross products\n\nIn case of cartesian/cross products an alias work as prefix, or table name, so that each individual \nelement on resulting tuples can still be referenced.\n\n```javascript\n  const ab = new CSetArray([1, 2]).as(\"a\").crossProduct(new CSetArray([1, 2, 3]).as(\"b\"));\n  const AB = ab.as(\"A\").crossProduct(ab.as(\"B\"));\n\n  console.log(AB.header); // \"A.a\", \"A.b\", \"B.a\", \"B.b\";\n```\n\n\n## Header\n\nIn case of cartesian/cross product it will return an array of alias (string), \nfor normal sets it will return one alias (string).\n\n```javascript\n\n    const A = new CSetArray([1, 2, 3]).as(\"A\");\n    const B = new CSetArray([1, 2, 3]).as(\"A\");\n\n    console.log(A.header); // [\"A\"]\n\n    const AxB = A.crossProduct(B);\n    console.log(AxB.header); // [\"A\", \"B\"]\n\n```\n\n## Select\n\nA select works as a filter on set elements, like other operators it creates a new set\nwhere all set elements must comply with provided constrains.\n\nSelect(alias, {name, predicate, partial})\n\n  * alias, an array containing name header of the restrictions,\n  * name, the name of the constrain, it can be any string, useful for JSON serialization.\n  * predicate, its a function defining a constrain, it has as arguments a value and outputs a boolean. \n  * partial, its similar to a constrain but it may be applied on partial values.\n\n  We must define at least a predicate or a partial function.\n\n```javascript\n  const a = new CSetArray([1, 3, 2]);\n  const b = a.union(new CSetArray([5, 3, 4])).as(\"AB\");\n\n  expect(a.count()).toBe(3);\n  expect(b.count()).toBe(5);\n\n  const ab = a.crossProduct(b); \n  expect(ab.count()).toBe(15);\n\n  const oddSum = a.as(\"A\").crossProduct(b.as(\"B\")).select(\n    [\"A\", \"B\"],\n    {\n      name: \"odd-sum\",\n      predicate: (A, B) =\u003e (A + B) % 2 === 1\n    }\n  );\n\n  for (let e of oddSum.values()) {\n    console.log(e);\n  }\n\n  /*\n    Output:\n      [ 1, 2 ]\n      [ 1, 4 ]\n      [ 3, 2 ]\n      [ 3, 4 ]\n      [ 2, 1 ]\n      [ 2, 3 ]\n      [ 2, 5 ]\n  */\n\n```\n\nUsing a partial and a predicate:\n\n\n```javascript\n      const A = new CSetArray([1, 2, 3, 4, 5, 7, 8, 9, 10]);\n\n      const B = A.as(\"a\").crossProduct(A.as(\"b\")).crossProduct(\n        A.as(\"c\").crossProduct(A.as(\"d\"))\n      )\n        .select([\"a\", \"b\", \"c\", \"d\"], {\n          name: \"\u003c\u003e\",\n          predicate: (a, b, c, d) =\u003e b === a + 1 \u0026\u0026 c === b + 1 \u0026\u0026 d === c + 1,\n\n          // headers the partial header, value is the partial value.\n          // eg. headers=[\"a\", \"b\"], values=[1, 2]\n          partial: (headers, value) =\u003e new Set(value).size === args.length\n        });\n\n      console.log([...B.values()]); // [[1,2,3,4],[2,3,4,5],[7,8,9,10]];\n```\n\nSame example with only partial definition:\n\n```javascript\n      const A = new CSetArray([1, 2, 3, 4, 5, 7, 8, 9, 10]);\n      const B = A.as(\"a\").crossProduct(A.as(\"b\")).crossProduct(\n        A.as(\"c\").crossProduct(A.as(\"d\"))\n      )\n        .select([\"a\", \"b\", \"c\", \"d\"], {\n          name: \"\u003c\u003e\",\n          partial: (headers, values) =\u003e {\n              const p = headers.map(h =\u003e [\"a\", \"b\", \"c\", \"d\"].indexOf(h));\n              const s = values[0];\n              for (let i=1; i\u003cvalues.length; i++) {\n                if (values[i] !== s + p[i]) {\n                  return false;\n                }\n              }\n\n              return true;\n            }\n        });\n\n      console.log([...B.values()]); // [[1,2,3,4],[2,3,4,5],[7,8,9,10]];\n```\n\n## Count\n\nIt counts the elements on a set.\n\n```javascript\n  const a = new CSetArray([1, 3, 2]);\n  const b = a.union(new CSetArray([5, 3, 4]));\n\n  console.log(a.count()); // 3\n```\n\n# Projection\n\nCreates a subset from original set with a restricted set of attributes.\n\n```javascript\n  const a = new CSetArray([1, 2]).as(\"a\");\n  const b = new CSetArray([3, 4]).as(\"b\");\n  const c = new CSetArray([5, 6]).as(\"c\");\n  const d = new CSetArray([7, 8]).as(\"d\");\n\n  const s = a.crossProduct(b).crossProduct(c).crossProduct(d);\n  \n  console.log([...s.projection(\"d\", \"b\").values()]); \n  /* Output:\n    [[7, 3], [8, 3], [7, 4], [8, 4]]\n  */\n```\n\n\n# Examples\n\nIn this section I just want to show a few examples on how CSet can be used, but some of examples may not be the best use \ncase for the lib (See Motivation section).\n\n## Puzzle: Send+More=Money\n\nSolve expression:\n\n      S E N D\n    + M O R E\n  ------------\n    M O N E Y\n\n\nWhere each letter on the expression is a digit (0..9) and all letters must have different values.\nSome people discard M=0 solutions, but in this case I will consider all solutions including M=0.\n\n```javascript\n\n  const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];\n  const d = new CSetArray(digits);\n  const letters = [\"S\", \"E\", \"N\", \"D\", \"M\", \"O\", \"R\", \"Y\"];\n\n  const s = letters.map(h =\u003e d.as(h)).reduce(\n    (s, e) =\u003e s?s.crossProduct(e):e\n  );\n\n  // S E N D M O R Y\n  const sendMoreMoney = s.select(\n    [\"S\", \"E\", \"N\", \"D\", \"M\", \"O\", \"R\", \"Y\"],\n    {\n      name: \"add\",\n      predicate: (S, E, N, D, M, O, R, Y) =\u003e \n          S * 1000 + E * 100 + N * 10 + D \n        + M * 1000 + O * 100 + R * 10  + E  \n          === \n          M * 10000 + O * 1000 + N * 100 + E * 10 + Y,\n      partial: (headers, values) =\u003e new Set(values).size === values.length\n    }\n  );\n\n  for (let [S, E, N, D, M, O, R, Y] of sendMoreMoney.values()) {\n    const send = S * 1000 + E * 100 + N * 10 + D;\n    const more = M * 1000 + O * 100 + R * 10  + E;\n    const money = M * 10000 + O * 1000 + N * 100 + E * 10 + Y;\n    console.log(`${send} + ${more} = ${money}`);\n  }\n```\n\nWith the use of partial filter, the problem is much more clean and optimized since we \nare filtering all values that are distinct.\n\n# Motivation\n\nI created CSet to try to find a way to handle domain combinatorial explosion.\nThe main design concept of CSet is to be lazy, do as little as possible and only do it on demand,\nby delaying evaluation and by failing sooner than later we can save processing time and memory.\n\nWhile memory and processing time is a concern of CSet design, not all combinatorial problems are\nsuited for CSet, CSet is meant to be used as a domain/set representation library, but not to be used \nfor example as a Constrain Solving Problem library. \n\n\n# Future Work\n\nI think I would like to grow CSet features, manly set theory stuff, and optimize the engine with a \nplanner, cache and some other database techniques.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffsvieira%2Fcset","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffsvieira%2Fcset","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffsvieira%2Fcset/lists"}