├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── license ├── package.json ├── readme.md ├── rollup.config.js ├── src └── index.js ├── test └── index.js └── zet.png /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = tab 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{json,yml}] 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | name: Node.js v${{ matrix.nodejs }} (${{ matrix.os }}) 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | nodejs: [8, 10, 12] 12 | os: [ubuntu-latest] 13 | steps: 14 | - uses: actions/checkout@v1 15 | with: 16 | fetch-depth: 1 17 | 18 | - uses: actions/setup-node@v1 19 | with: 20 | node-version: ${{ matrix.nodejs }} 21 | 22 | - name: Install 23 | run: | 24 | npm install 25 | npm install -g nyc 26 | 27 | - name: Test w/ Coverage 28 | run: npx nyc --include=src npm test 29 | 30 | - name: Report 31 | if: matrix.nodejs >= 12 32 | run: | 33 | npx nyc report --reporter=text-lcov > coverage.lcov 34 | bash <(curl -s https://codecov.io/bash) 35 | env: 36 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | .DS_Store 4 | *.lock 5 | *.log 6 | 7 | /sync 8 | /dist 9 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Terkel Gjervig (terkel.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zet", 3 | "version": "1.1.0", 4 | "repository": "terkelg/zet", 5 | "description": "Like Set() but with union, intersect, difference and more built-in", 6 | "module": "dist/zet.mjs", 7 | "main": "dist/zet.js", 8 | "browser": "dist/zet.umd.js", 9 | "license": "MIT", 10 | "files": [ 11 | "dist" 12 | ], 13 | "author": { 14 | "name": "Terkel Gjervig", 15 | "email": "terkel@terkel.com", 16 | "url": "https://terkel.com" 17 | }, 18 | "scripts": { 19 | "dev": "rollup -c -w", 20 | "build": "rollup -c", 21 | "pretest": "npm run build", 22 | "prepublishOnly": "npm run build", 23 | "test": "tape -r esm test/*.js | tap-spec" 24 | }, 25 | "keywords": [ 26 | "set", 27 | "math", 28 | "sets", 29 | "union", 30 | "intersect", 31 | "difference" 32 | ], 33 | "devDependencies": { 34 | "esm": "^3.2.25", 35 | "rollup": "^1.26.4", 36 | "tap-spec": "^5.0.0", 37 | "tape": "^4.11.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | zet 3 |

4 | 5 |

6 | 7 | version 8 | 9 | 10 | build status 11 | 12 | 13 | codecov 14 | 15 | 18 | 19 | install size 20 | 21 |

22 | 23 |

JavaScript Set() as it should be.

24 | 25 | ECMAScript 6 sets have no methods for computing the union (∪), intersection (∩) or difference (⊖). 26 | Zet is an extension of ES6 [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) and comes with all its functionality included with extra set logic. 27 | The API is similar to how sets work in [Python](https://docs.python.org/2/library/stdtypes.html#set). 28 | 29 | 30 | ## Features 31 | 32 | Additions to the default ECMAScript 6 set 33 | 34 | - Union (∪) 35 | - Intersection (∩) 36 | - Difference/subtract (-) 37 | - Symmetric difference (⊖) 38 | - Subset (⊆) 39 | - Superset (⊇) 40 | - Map 41 | - Filter 42 | - Reduce 43 | 44 | 45 | Additionally, this module is delivered as: 46 | 47 | * **ES Module**: `dist/zet.mjs` 48 | * **CommonJS**: `dist/zet.js` 49 | * **UMD**: `dist/zet.umd.js` 50 | 51 | 52 | ## Install 53 | 54 | ``` 55 | $ npm install --save zet 56 | ``` 57 | 58 | 59 | ## Usage 60 | 61 | ```js 62 | import Zet from 'zet'; 63 | 64 | let a = new Zet([1, 2, 3]); 65 | let b = new Zet([3, 4, 5]); 66 | let c = new Zet([2, 3, 4]); 67 | 68 | Zet.union(a, b); 69 | //=> [Zet] {1, 2, 3, 4, 5} 70 | 71 | a.union(b, c); 72 | //=> [Zet] {1, 2, 3, 4, 5} 73 | 74 | a.intersection(b); 75 | //=> [Zet] {3} 76 | 77 | a.symmetricDifference(c); 78 | //=> [Zet] {1, 4} 79 | 80 | a.subset(b); 81 | //=> false 82 | 83 | a.filter(i => i % 2); 84 | //=> [Zet] {1, 3} 85 | 86 | ``` 87 | 88 | 89 | # API 90 | 91 | ## Zet([iterable]) 92 | Returns:`Zet` 93 | 94 | Returns the Zet instance. 95 | 96 | **Zet extends [`Set()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) and inherit all its functionality, like `has()`, `size()` etc.** 97 | 98 | ### Zet.union(...sets) ∪ 99 | Returns:`zet` 100 | 101 | Static variadic function that return a new set with elements from all other `sets`. 102 | 103 | #### sets 104 | Type: `Zet|Set` 105 | 106 | Two or more sets of type `Zet` or `Set`. 107 | 108 | ### Zet.intersection(...sets) ∩ 109 | Returns:`zet` 110 | 111 | Static variadic function that return a new set with elements common to `this` and all other `sets`. 112 | 113 | #### sets 114 | Type: `Zet|Set` 115 | 116 | Two or more sets of type `Zet` or `Set`. 117 | 118 | ### Zet.difference(...sets) - or \\ 119 | Returns:`zet` 120 | 121 | Returns the difference between two or more sets. The order of the sets matters. Sets are differentiated against the first argument/set. 122 | 123 | #### sets 124 | Type: `Zet|Set` 125 | 126 | Two or more sets of type `Zet` or `Set`. 127 | 128 | ### Zet.symmetricDifference(setA, setB) ⊖ or ∆ 129 | Returns:`zet` 130 | 131 | Static function that return a new set with elements in either setA or setB but not both. 132 | 133 | #### setA 134 | Type: `Zet|Set` 135 | 136 | Set A of type `Zet` or `Set`. 137 | 138 | #### setB 139 | Type: `Zet|Set` 140 | 141 | Set B of type `Zet` or `Set`. 142 | 143 | ### Zet.subset(setA, setB) 144 | Returns: `Boolean` 145 | 146 | Test whether every element in `setB` is in `setA`. 147 | 148 | #### setA 149 | Type: `Zet|Set` 150 | 151 | Set of type `Zet` or `Set`. 152 | 153 | #### setB 154 | Type: `Zet|Set` 155 | 156 | Set of type `Zet` or `Set`. 157 | 158 | ### Zet.superset(setA, setB) 159 | Returns: `Boolean` 160 | 161 | Test whether every element in `setA` is in `setB`. 162 | 163 | #### setA 164 | Type: `Zet|Set` 165 | 166 | Set of type `Zet` or `Set`. 167 | 168 | #### setB 169 | Type: `Zet|Set` 170 | 171 | Set of type `Zet` or `Set`. 172 | 173 | ### map(set, func) 174 | Returns: `Zet|Set` 175 | 176 | Creates a set with the results of calling the provided function on every element. 177 | 178 | #### set 179 | Type: `Zet|Set` 180 | 181 | Set of type `Zet` or `Set`. 182 | 183 | #### func 184 | Type: `Function` 185 | 186 | Function that produces an element of the new set. 187 | 188 | ### filter(set, func) 189 | Returns: `Zet|Set` 190 | 191 | Creates a set with all elements that pass the test implemented by the provided function. 192 | 193 | #### set 194 | Type: `Zet|Set` 195 | It is the set going to be examined. 196 | 197 | #### func 198 | Type: `Function` 199 | 200 | It is a predicate, to test each element of the set. 201 | 202 | ### reduce(set, func, *initializer*) 203 | Returns: `Number` 204 | 205 | Reduces the set to a single value, by executing the provided function for each element in the set (from left-to-right). 206 | 207 | #### set 208 | Type: `Zet|Set` 209 | 210 | Set of type `Zet` or `Set`. 211 | 212 | #### func 213 | Type: `Function` 214 | 215 | Function to be executed for each element in the set. 216 | 217 | #### *initializer* 218 | Type: `Number` 219 | 220 | Optional. A value to be passed to the function as the initial value. 221 | 222 | ## Instance Methods 223 | 224 | ### union(...sets) ∪ 225 | Returns:`zet` 226 | 227 | Variadic method that return a new set with elements from `this` and all other `sets`. 228 | 229 | #### sets 230 | Type: `Zet|Set` 231 | 232 | One or more sets of type `Zet` or `Set`. 233 | 234 | ### intersection(...sets) ∩ 235 | Returns:`zet`
236 | 237 | Variadic method that return a new set with elements common to `this` and all other `sets`. 238 | 239 | #### sets 240 | Type: `Zet|Set` 241 | 242 | One or more sets of type `Zet` or `Set`. 243 | 244 | ### difference(...sets) - or \\ 245 | Returns:`zet`
246 | 247 | Variadic method tht return a new set with elements in `this` that are not in the other `sets`. 248 | 249 | #### sets 250 | Type: `Zet|Set` 251 | 252 | One or more sets of type `Zet` or `Set`. 253 | 254 | ### symmetricDifference(other) ⊖ or ∆ 255 | Returns:`zet` 256 | 257 | Method that return a new set with elements in either `this` or `other` but not both. This is also known as xor. 258 | 259 | #### other 260 | Type: `Zet|Set` 261 | 262 | Set of type `Zet` or `Set`. 263 | 264 | ### subset(other) 265 | Returns: `Boolean` 266 | 267 | Test whether every element in the set is in `other`. 268 | 269 | #### other 270 | Type: `Zet|Set` 271 | 272 | Set of type `Zet` or `Set`. 273 | 274 | ### superset(other) 275 | Returns: `Boolean` 276 | 277 | Test whether every element in `other` is in the set. 278 | 279 | #### other 280 | Type: `Zet|Set` 281 | 282 | Set of type `Zet` or `Set`. 283 | 284 | ### map(func) 285 | Returns: `Zet|Set` 286 | 287 | Creates a set with the results of calling the provided function on every element. 288 | 289 | #### func 290 | Type: `Function` 291 | 292 | Function that produces an element of the new set. 293 | 294 | ### filter(func) 295 | Returns: `Zet|Set` 296 | 297 | Creates a set with all elements that pass the test implemented by the provided function. 298 | 299 | #### func 300 | Type: `Function` 301 | 302 | It is a predicate, to test each element of the set. 303 | 304 | ### reduce(func, *initializer*) 305 | Returns: `Number` 306 | 307 | Reduces the set to a single value, by executing the provided function for each element in the set (from left-to-right). 308 | 309 | #### func 310 | Type: `Function` 311 | 312 | Function to be executed for each element in the set. 313 | 314 | #### *initializer* 315 | Type: `Number` 316 | 317 | Optional. A value to be passed to the function as the initial value. 318 | 319 | ## License 320 | 321 | MIT © [Terkel Gjervig](https://terkel.com) 322 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | 3 | export default [ 4 | { 5 | input: 'src/index.js', 6 | output: [ 7 | { file: pkg.main, format: 'cjs' }, 8 | { file: pkg.module, format: 'es' }, 9 | { file: pkg.browser, format: 'umd', name: 'zet' } 10 | ] 11 | } 12 | ]; 13 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export default class Zet extends Set { 2 | static union(...sets) { 3 | return new Zet(sets.reduce((a, i) => [...a, ...i])); 4 | } 5 | 6 | static intersection(...sets) { 7 | sets = sets.map(s => [...s]); 8 | return new Zet(sets.reduce((prev, curr) => prev.filter(x => curr.includes(x)))); 9 | } 10 | 11 | static difference(...sets) { 12 | if (sets.length === 1) return new Zet(); 13 | sets = sets.map(s => [...s]); 14 | return new Zet(sets.reduce((prev, curr) => prev.filter(x => !curr.includes(x)))); 15 | } 16 | 17 | static symmetricDifference(setA=new Zet(), setB=new Zet()) { 18 | return new Zet([...[...setA].filter(x => !setB.has(x)), ...[...setB].filter(x => !setA.has(x))]); 19 | } 20 | 21 | static subset(setA, setB) { 22 | return [...setA].every(x => setB.has(x)); 23 | } 24 | 25 | static superset(setA, setB) { 26 | return [...setB].every(x => setA.has(x)); 27 | } 28 | 29 | static map(set, func) { 30 | return new Zet([...set].map(func)); 31 | } 32 | 33 | static filter(set, func) { 34 | return new Zet([...set].filter(func)); 35 | } 36 | 37 | static reduce(set, func, initializer) { 38 | if (initializer === undefined) return [...set].reduce(func); 39 | return [...set].reduce(func, initializer); 40 | } 41 | 42 | union(...sets) { 43 | return Zet.union(this, ...sets); 44 | } 45 | 46 | intersection(...sets) { 47 | return Zet.intersection(this, ...sets); 48 | } 49 | 50 | difference(...sets) { 51 | return Zet.difference(this, ...sets); 52 | } 53 | 54 | symmetricDifference(other) { 55 | return Zet.symmetricDifference(this, other); 56 | } 57 | 58 | subset(other) { 59 | return Zet.subset(this, other); 60 | } 61 | 62 | superset(other) { 63 | return Zet.superset(this, other); 64 | } 65 | 66 | map(func) { 67 | return Zet.map(this, func); 68 | } 69 | 70 | filter(func) { 71 | return Zet.filter(this, func); 72 | } 73 | 74 | reduce(func, initializer) { 75 | return Zet.reduce(this, func, initializer); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import test from 'tape'; 2 | import Zet from '../src'; 3 | 4 | const eq = (t, a, b) => { 5 | a = [...a.values()]; 6 | return t.deepEqual(a, b); 7 | }; 8 | 9 | test('exports', t => { 10 | t.is(typeof Zet, 'function', '~> a function'); 11 | t.end(); 12 | }); 13 | 14 | test('basic', t => { 15 | let x = new Zet([1, 2, 2, 3, 4]); 16 | t.is(x.size, 4); 17 | t.ok(x.has(2)); 18 | t.end(); 19 | }); 20 | 21 | test('static :: union ∪', t => { 22 | let x = new Zet([1,2,3]); 23 | let y = new Zet([2,3,4]); 24 | let z = new Zet([4,5,6]); 25 | eq(t, Zet.union(x), [1,2,3]); 26 | eq(t, Zet.union(x, y), [1,2,3,4]); 27 | eq(t, Zet.union(x, y, z), [1,2,3,4,5,6]); 28 | t.end(); 29 | }); 30 | 31 | test('static :: intersection ∩', t => { 32 | let x = new Zet([1,2,3]); 33 | let y = new Zet([2,3,4]); 34 | let z = new Zet([3,4,5]); 35 | eq(t, Zet.intersection(x), [1,2,3]); 36 | eq(t, Zet.intersection(x, y), [2,3]); 37 | eq(t, Zet.intersection(x, y, z), [3]); 38 | t.end(); 39 | }); 40 | 41 | test('static :: difference -', t => { 42 | let x = new Zet([1,2,3]); 43 | let y = new Zet([4,3,2]); 44 | let z = new Zet([3,4,5]); 45 | let w = new Zet([1,6,7]); 46 | eq(t, Zet.difference(x), []); 47 | eq(t, Zet.difference(x, y), [1]); 48 | eq(t, Zet.difference(x, z), [1, 2]); 49 | eq(t, Zet.difference(x, y, z), [1]); 50 | eq(t, Zet.difference(x, y, z, w), []); 51 | t.end(); 52 | }); 53 | 54 | test('static :: symmetricDifference \\', t => { 55 | let x = new Zet([1,2,3]); 56 | let y = new Zet([4,3,2]); 57 | let z = new Zet([3,4,5]); 58 | let w = new Zet([1,6,7]); 59 | eq(t, Zet.symmetricDifference(x), [1,2,3]); 60 | eq(t, Zet.symmetricDifference(x, y), [1,4]); 61 | eq(t, Zet.symmetricDifference(x, z), [1,2,4,5]); 62 | eq(t, Zet.symmetricDifference(z, w), [3,4,5,1,6,7]); 63 | t.end(); 64 | }); 65 | 66 | test('static :: subset ⊆', t => { 67 | let x = new Zet([1,2]); 68 | let y = new Zet([1,2,3]); 69 | t.ok(Zet.subset(x,y)); 70 | t.ok(Zet.subset(x,x)); 71 | t.notOk(Zet.subset(y,x)); 72 | t.end(); 73 | }); 74 | 75 | test('static :: superset ⊇', t => { 76 | let x = new Zet([1,2,3]); 77 | let y = new Zet([1,2]); 78 | t.ok(Zet.superset(x,y)); 79 | t.ok(Zet.superset(x,x)); 80 | t.notOk(Zet.superset(y,x)); 81 | t.end(); 82 | }); 83 | 84 | test('static :: map', t => { 85 | let x = new Zet([1,4,9,16]); 86 | eq(t, Zet.map(x, i => i * 2), [2,8,18,32]); 87 | t.end(); 88 | }); 89 | 90 | test('static :: filter', t => { 91 | let x = new Zet([1,2,3]); 92 | eq(t, Zet.filter(x, i => i % 2), [1, 3]); 93 | t.end(); 94 | }); 95 | 96 | test('static :: reduce', t => { 97 | let x = new Zet([1,2,3,4]); 98 | t.is(Zet.reduce(x, (i, j) => i + j), 10); 99 | t.is(Zet.reduce(x, (i, j) => i + j, 5), 15); 100 | t.end(); 101 | }); 102 | 103 | test('instance :: union ∩', t => { 104 | let x = new Zet([1,2,3]); 105 | let y = new Zet([2,3,4]); 106 | let z = new Zet([4,5,6]); 107 | eq(t, x.union(), [1,2,3]); 108 | eq(t, x.union(y), [1,2,3,4]); 109 | eq(t, x.union(y, z), [1,2,3,4,5,6]); 110 | t.end(); 111 | }); 112 | 113 | test('instance :: intersection ∩', t => { 114 | let x = new Zet([1,2,3]); 115 | let y = new Zet([2,3,4]); 116 | let z = new Zet([3,4,5]); 117 | eq(t, x.intersection(), [1,2,3]); 118 | eq(t, x.intersection(y), [2,3]); 119 | eq(t, x.intersection(y, z), [3]); 120 | t.end(); 121 | }); 122 | 123 | test('instance :: difference -', t => { 124 | let x = new Zet([1,2,3]); 125 | let y = new Zet([4,3,2]); 126 | let z = new Zet([3,4,5]); 127 | let w = new Zet([1,6,7]); 128 | eq(t, x.difference(), []); 129 | eq(t, x.difference(y), [1]); 130 | eq(t, x.difference(z), [1, 2]); 131 | eq(t, x.difference(y, z), [1]); 132 | eq(t, x.difference(y, z, w), []); 133 | t.end(); 134 | }); 135 | 136 | test('instance :: symmetricDifference \\', t => { 137 | let x = new Zet([1,2,3]); 138 | let y = new Zet([4,3,2]); 139 | let z = new Zet([3,4,5]); 140 | let w = new Zet([1,6,7]); 141 | eq(t, x.symmetricDifference(), [1,2,3]); 142 | eq(t, x.symmetricDifference(y), [1,4]); 143 | eq(t, x.symmetricDifference(z), [1,2,4,5]); 144 | eq(t, z.symmetricDifference(w), [3,4,5,1,6,7]); 145 | t.end(); 146 | }); 147 | 148 | test('instance :: subset ⊆', t => { 149 | let x = new Zet([1,2]); 150 | let y = new Zet([1,2,3]); 151 | t.ok(x.subset(y)); 152 | t.ok(x.subset(x)); 153 | t.notOk(y.subset(x)); 154 | t.end(); 155 | }); 156 | 157 | test('instance :: superset ⊇', t => { 158 | let x = new Zet([1,2,3]); 159 | let y = new Zet([1,2]); 160 | t.ok(x.superset(y)); 161 | t.ok(x.superset(x)); 162 | t.notOk(y.superset(x)); 163 | t.end(); 164 | }); 165 | 166 | test('instance :: map', t => { 167 | let x = new Zet([1,4,9,16]); 168 | eq(t, x.map(i => i * 2), [2,8,18,32]); 169 | t.end(); 170 | }); 171 | 172 | test('instance :: filter', t => { 173 | let x = new Zet([1,2,3]); 174 | eq(t, x.filter(i => i % 2), [1, 3]); 175 | t.end(); 176 | }); 177 | 178 | test('instance :: reduce', t => { 179 | let x = new Zet([1,2,3,4]); 180 | t.is(x.reduce((i, j) => i + j), 10); 181 | t.is(x.reduce((i, j) => i + j, 5), 15); 182 | t.end(); 183 | }); 184 | -------------------------------------------------------------------------------- /zet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terkelg/zet/8d77f9d27c432ecd3c088b2c242f28cbe8ed9cf2/zet.png --------------------------------------------------------------------------------