├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── getMatchDetails.js ├── match.js ├── matchArray.js ├── matchObject.js ├── objectEquals.js ├── option.js ├── utils.js └── z.js ├── tests ├── arrays.spec.js ├── matchObject.spec.js ├── matches.spec.js ├── objectEquals.spec.js ├── objects.spec.js ├── outsideScope.spec.js ├── regex.spec.js ├── types.spec.js ├── usages.spec.js └── valueComparsion.spec.js └── z-logo.png /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | # editorconfig-tools is unable to ignore longs strings or urls 11 | max_line_length = null -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 29 | node_modules 30 | 31 | .nyc_output/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.2" 4 | script: 5 | - npm run build 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![z](https://raw.githubusercontent.com/leonardiwagner/z/master/z-logo.png) Pattern matching for JavaScript 2 | 3 | [![Build Status](https://travis-ci.org/z-pattern-matching/z.svg?branch=master)](https://travis-ci.org/z-pattern-matching/z) 4 | [![Coverage Status](https://coveralls.io/repos/github/z-pattern-matching/z/badge.svg?branch=master)](https://coveralls.io/github/z-pattern-matching/z?branch=master) 5 | [![NPM version](https://img.shields.io/npm/v/z.svg)](https://www.npmjs.com/package/z) 6 | 7 | ### Usage 8 | - Install via npm: `npm install z` 9 | - Require z in your code and use the matches function: `const { matches } = require('z')` 10 | 11 | ### Avaiable Patterns 12 | 13 | - Matches by value: `(x = 1) =>, (x = null) =>, (x = 'true') =>` 14 | - Matches by object or array: `(x = {a: 1}) =>, (x = [1, 2]) =>` 15 | - Matches by type: `(x = String) =>, (x = Boolean) =>` 16 | - Matches by instance: `(x = Date) =>, (x = Person) =>` 17 | - Matches by splitting array into elements and tail `(head, tail) =>` , `(a, b, c, tail) =>`, etc… 18 | 19 | 20 | ### Examples 21 | - **Example:** Matches by Object property 22 | ```javascript 23 | const { matches } = require('z') 24 | 25 | const person = { name: 'Maria' } 26 | matches(person)( 27 | (x = { name: 'John' }) => console.log('John you are not welcome!'), 28 | (x) => console.log(`Hey ${x.name}, you are welcome!`) 29 | ) 30 | 31 | //output: `Hey Maria, you are welcome!` 32 | ``` 33 | 34 | - **Example:** Matches by type or instances 35 | ```javascript 36 | const { matches } = require('z') 37 | 38 | const result = matches(1)( 39 | (x = 2) => 'number 2 is the best!!!', 40 | (x = Number) => `number ${x} is not that good`, 41 | (x = Date) => 'blaa.. dates are awful!' 42 | ) 43 | 44 | console.log(result) // output: number 1 is not that good 45 | ``` 46 | 47 | - **Example:** matches Array content 48 | 49 | > To match array content you need create multiple arguments for the match function, such as (a, b, c, tail) => {} , then each variable match each item from array. Note: last variable contains all remaining array items, formally named tail. Examples: 50 | ```javascript 51 | const { matches } = require('z') 52 | 53 | matches([1, 2, 3, 4, 5])( 54 | (a, b, c, tail) => 'a = 1, b = 2, c = 3, tail = [4, 5]' 55 | ) 56 | 57 | matches([1, 2])( 58 | (a, tail) => 'a = 1, b = [2]' 59 | ) 60 | 61 | matches([1])( 62 | (a, b, tail) => 'Will not match here', 63 | (a = 2, tail = []) => 'Will not match here', 64 | (a = 1, tail = []) => 'Will match here, tail = []' 65 | ) 66 | ``` 67 | 68 | - **Example:** Powerful recursive code which will remove sequential repeated items from Array. 69 | 70 | > Can be mind blowing if it’s the first time you meet pattern matching, but you are gonna understand it! 71 | ```javascript 72 | const { matches } = require('z') 73 | 74 | const compress = (numbers) => { 75 | return matches(numbers)( 76 | (x, y, xs) => x === y 77 | ? compress([x].concat(xs)) 78 | : [x].concat(compress([y].concat(xs))), 79 | (x, [y]) => x === y // stopping condition 80 | ? [x] 81 | : [x, y], 82 | x => x 83 | ) 84 | } 85 | 86 | compress([1, 1, 2, 3, 4, 4, 4]) //output: [1, 2, 3, 4] 87 | ``` 88 | 89 | ### License 90 | 91 | [Apache 2.0][apache-license] 92 | 93 | [apache-license]:./LICENSE 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "z", 3 | "version": "1.0.8", 4 | "main": "src/z.js", 5 | "description": "native pattern matching for javascript", 6 | "scripts": { 7 | "test": "NODE_PATH=. mocha **/*.spec.js", 8 | "test:debug": "NODE_PATH=. mocha --inspect-brk **/*.spec.js", 9 | "test:watch": "npm test -- --watch", 10 | "test:min": "npm run test:watch -- --reporter min", 11 | "lint": "standard --fix '**/*.js' | snazzy", 12 | "coverage": "nyc npm test", 13 | "format": "prettier-std --write 'src/*.js'", 14 | "build": "npm run lint && npm test && npm run coverage" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/z-pattern-matching/z" 19 | }, 20 | "author": "Wagner Leonardi", 21 | "keywords": [ 22 | "pattern", 23 | "matching", 24 | "functional", 25 | "haskell" 26 | ], 27 | "standard": { 28 | "globals": [ 29 | "describe", 30 | "it", 31 | "after", 32 | "afterEach", 33 | "before", 34 | "beforeEach" 35 | ] 36 | }, 37 | "nyc": { 38 | "statements": 98.53, 39 | "branches": 98.08, 40 | "functions": 100, 41 | "lines": 98.5, 42 | "check-coverage": true, 43 | "reporter": [ 44 | "lcov", 45 | "text", 46 | "text-summary" 47 | ], 48 | "exclude": [ 49 | "node_modules/**/*", 50 | "**/*.spec.js", 51 | "src/utils.js" 52 | ] 53 | }, 54 | "bugs": { 55 | "url": "https://github.com/z-pattern-matching/z/issues" 56 | }, 57 | "homepage": "https://github.com/z-pattern-matching/z#readme", 58 | "license": "Apache-2.0", 59 | "devDependencies": { 60 | "chai": "^3.4.0", 61 | "coveralls": "^2.11.16", 62 | "husky": "^0.14.3", 63 | "lint-staged": "^4.0.1", 64 | "mocha": "^5.2.0", 65 | "mocha-lcov-reporter": "^1.2.0", 66 | "nyc": "^10.1.2", 67 | "prettier-std-cli": "^1.0.0", 68 | "snazzy": "^7.0.0", 69 | "standard": "^8.6.0" 70 | }, 71 | "dependencies": { 72 | "deep-equal": "^1.0.1", 73 | "flat": "^2.0.1", 74 | "install": "^0.10.0", 75 | "js-function-reflector": "git+https://github.com/leonardiwagner/js-function-reflector.git" 76 | }, 77 | "engines": { 78 | "node": ">= 6.0.0" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/getMatchDetails.js: -------------------------------------------------------------------------------- 1 | const functionReflector = require('js-function-reflector') 2 | 3 | module.exports = function (matchFunction) { 4 | const reflectedFunction = functionReflector.call(this, matchFunction) 5 | 6 | return { 7 | args: reflectedFunction.args, 8 | func: matchFunction 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/match.js: -------------------------------------------------------------------------------- 1 | const deepEqual = require('deep-equal') 2 | const objectEquals = require('./objectEquals') 3 | const option = require('./option') 4 | 5 | module.exports = (match, subjectToMatch) => { 6 | const hasMatchValue = match.args[0].length >= 2 7 | if (!hasMatchValue) { 8 | return option.Some(subjectToMatch) 9 | } 10 | 11 | const matchValue = match.args[0][1] 12 | 13 | // if is a type check, check type 14 | if (matchValue === Boolean && typeof subjectToMatch === 'boolean') { 15 | return option.Some(subjectToMatch) 16 | } 17 | if (matchValue === undefined && typeof subjectToMatch === 'undefined') { 18 | return option.Some(subjectToMatch) 19 | } 20 | if (matchValue === Number && typeof subjectToMatch === 'number') { 21 | return option.Some(subjectToMatch) 22 | } 23 | if (matchValue === String && typeof subjectToMatch === 'string') { 24 | return option.Some(subjectToMatch) 25 | } 26 | if (matchValue === Object && typeof subjectToMatch === 'object') { 27 | return option.Some(subjectToMatch) 28 | } 29 | 30 | // if is instance check, check instance 31 | if ( 32 | typeof matchValue === 'function' && 33 | subjectToMatch instanceof matchValue 34 | ) { 35 | return option.Some(subjectToMatch) 36 | } 37 | 38 | // if it is a regex, and the value is a string, test that it matches 39 | if (matchValue instanceof RegExp && typeof subjectToMatch === 'string') { 40 | if (matchValue.test(subjectToMatch)) { 41 | return option.Some(subjectToMatch) 42 | } 43 | } 44 | 45 | // if its array 46 | if ( 47 | subjectToMatch instanceof Array && 48 | matchValue instanceof Array 49 | ) { 50 | if (deepEqual(subjectToMatch, matchValue)) { 51 | return option.Some(subjectToMatch) 52 | } 53 | } 54 | 55 | // if is object (and not an array), check if contains 56 | if (typeof subjectToMatch === 'object' && subjectToMatch !== null && !(subjectToMatch instanceof Array) && typeof matchValue === 'object') { 57 | if (objectEquals(matchValue, subjectToMatch)) { 58 | return option.Some(subjectToMatch) 59 | } 60 | } 61 | 62 | // if is value check, check value 63 | if (subjectToMatch === matchValue) { 64 | return option.Some(subjectToMatch) 65 | } 66 | 67 | return option.None 68 | } 69 | -------------------------------------------------------------------------------- /src/matchArray.js: -------------------------------------------------------------------------------- 1 | const deepEqual = require('deep-equal') 2 | const option = require('./option') 3 | const match = require('./match') 4 | 5 | module.exports = (currentMatch, subjectToMatch) => { 6 | const matchArgs = currentMatch.args.map( 7 | (x, index) => 8 | Array.isArray(x) ? { key: x[0], value: x[1], index } : { key: x, index } 9 | ) 10 | 11 | if (subjectToMatch.length < matchArgs.length) { 12 | const matchOnSubArg = (arg, toMatch) => 'value' in arg 13 | ? deepEqual(arg.value, toMatch) 14 | : true 15 | 16 | const matchAllSubArgs = matchArgs 17 | .slice(0, matchArgs.length - 1) 18 | .every((arg, index) => matchOnSubArg(arg, subjectToMatch[index])) 19 | 20 | if (matchAllSubArgs && deepEqual(matchArgs[matchArgs.length - 1].value, [])) { 21 | return option.Some(subjectToMatch[0]) 22 | } 23 | 24 | return option.None 25 | } 26 | 27 | const heads = Array.from( 28 | Array(matchArgs.length - 1), 29 | (x, y) => subjectToMatch[y] 30 | ) 31 | const tail = subjectToMatch.slice(matchArgs.length - 1) 32 | 33 | // CAUTION: it gets the tail arg and removes from matchArgs (due splice function) 34 | const [tailArg] = matchArgs.splice(matchArgs.length - 1, 1) 35 | if (tailArg.value) { 36 | const matchObject = { args: [[undefined, tailArg.value]] } 37 | const matchResult = match(matchObject, tail) 38 | if (matchResult === option.None) { 39 | return option.None 40 | } 41 | } 42 | 43 | const headsWithArgs = matchArgs.filter(x => x.value) 44 | for (let i = 0; i < headsWithArgs.length; i++) { 45 | const matchObject = { 46 | args: [[headsWithArgs[i].key, headsWithArgs[i].value]] 47 | } 48 | 49 | const matchResult = match(matchObject, heads[headsWithArgs[i].index]) 50 | if (matchResult === option.None) { 51 | return option.None 52 | } 53 | } 54 | 55 | return option.Some(heads.concat([tail])) 56 | } 57 | -------------------------------------------------------------------------------- /src/matchObject.js: -------------------------------------------------------------------------------- 1 | const flatten = require('flat') 2 | const { checkArray, compose, containsAll, isChar } = require('./utils') 3 | 4 | // This is just an approach for deriving an actual js object 5 | // from the punned syntax of object destructuring in the 6 | // function argument reflection, that object can then 7 | // be used to check the keys of the subjectToMatch 8 | const buildSpecFromReflectedArgs = str => 9 | [...str].reduce((res, curr, i) => { 10 | switch (true) { 11 | // add a dummy value when a key without a value is found 12 | case /(,|})/.test(curr) && isChar(str.charAt(i - 1)): 13 | return res.concat('":1').concat(curr) 14 | 15 | // add a opening quote to keynames that are missing them 16 | case isChar(curr) && !isChar(str.charAt(i - 1)): 17 | // add a closing quote to keynames that are missing them 18 | /* falls through */ 19 | case curr === ':' && str.charAt(i - 1) !== '"': 20 | return res.concat('"').concat(curr) 21 | 22 | default: 23 | return res.concat(curr) 24 | } 25 | }, '') 26 | 27 | // derive a flattened list of keys|paths from an object 28 | const getFlattenedKeys = compose(Object.keys, flatten) 29 | 30 | const getFlattenedKeysFromArgs = compose( 31 | getFlattenedKeys, 32 | JSON.parse, 33 | // add dummy values so an object can be parsed from the args 34 | buildSpecFromReflectedArgs, 35 | // join the args back into original string 36 | xs => xs.join(','), 37 | // throw an error if passed a non array 38 | checkArray 39 | ) 40 | 41 | const objectAndArgsDestructureMatches = (reflectedArgs, subjectToMatch) => 42 | containsAll( 43 | getFlattenedKeysFromArgs(reflectedArgs), 44 | getFlattenedKeys(subjectToMatch) 45 | ) 46 | 47 | module.exports = { 48 | getFlattenedKeysFromArgs, 49 | objectAndArgsDestructureMatches 50 | } 51 | -------------------------------------------------------------------------------- /src/objectEquals.js: -------------------------------------------------------------------------------- 1 | const deepEqual = require('deep-equal') 2 | 3 | function getPropByString (obj, propString) { 4 | if (!propString) { 5 | return obj 6 | } 7 | 8 | let i 9 | let iLen 10 | let prop 11 | let props = propString.split('.') 12 | 13 | for (i = 0, iLen = props.length - 1; i < iLen; i++) { 14 | prop = props[i] 15 | 16 | const candidate = obj[prop] 17 | if (candidate !== undefined) { 18 | obj = candidate 19 | } else { 20 | break 21 | } 22 | } 23 | return obj[props[i]] 24 | } 25 | 26 | module.exports = (a, b) => { 27 | const objectEquals = (objectA, nestedKeys = []) => { 28 | if (objectA.constructor === Object) { 29 | const keys = Object.keys(objectA) 30 | 31 | for (var i in keys) { 32 | const key = keys[i] 33 | const nestedClone = nestedKeys.slice(0) 34 | nestedClone.push(key) 35 | 36 | const valueA = getPropByString(a, nestedClone.join('.')) 37 | const valueB = getPropByString(b, nestedClone.join('.')) 38 | 39 | if ( 40 | !(valueA instanceof Object) && 41 | !(valueB instanceof Object) && 42 | valueA !== valueB 43 | ) { 44 | return false 45 | } 46 | 47 | if ( 48 | (valueA instanceof Array) && 49 | (valueB instanceof Array) && 50 | !deepEqual(valueA, valueB) 51 | ) { 52 | return false 53 | } 54 | 55 | const partialResult = objectEquals(objectA[key], nestedClone) 56 | if (partialResult === false) { 57 | return false 58 | } 59 | } 60 | } 61 | 62 | return true 63 | } 64 | 65 | return objectEquals(a) 66 | } 67 | -------------------------------------------------------------------------------- /src/option.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Some: (value) => ({ value, hasValue: true }), 3 | None: {} 4 | } 5 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | // runtime type check for debugging purposes 2 | const checkArray = xs => { 3 | if (!Array.isArray(xs)) { 4 | throw new Error('matchObject expects a list of strings') 5 | } 6 | 7 | return xs 8 | } 9 | 10 | // standard compose function 11 | 12 | const compose = (...fns) => x => fns.reduceRight((v,f) => f(v) , x) 13 | 14 | // intended to take a single char and return true if it is a letter or number 15 | const isChar = x => /[a-zA-Z0-9]/.test(x) 16 | 17 | // takes two lists and returns true if all of the elements in the first 18 | // list are present in the second list 19 | const containsAll = (xs, ys) => 20 | xs.map(x => y => y.includes(x)).reduce((res, f) => f(ys) && res, true) 21 | 22 | // checks if a reflected list of arguments contains 23 | // object destructuring syntax 24 | const hasDestructuredObjectArguments = xs => 25 | xs.some(x => /({|})/.test(x) && !/function/.test(x)) 26 | 27 | module.exports = { 28 | hasDestructuredObjectArguments, 29 | checkArray, 30 | containsAll, 31 | compose, 32 | isChar 33 | } 34 | -------------------------------------------------------------------------------- /src/z.js: -------------------------------------------------------------------------------- 1 | const getMatchDetails = require('./getMatchDetails') 2 | const match = require('./match') 3 | const matchArray = require('./matchArray') 4 | const { hasDestructuredObjectArguments } = require('./utils') 5 | const { objectAndArgsDestructureMatches } = require('./matchObject') 6 | 7 | const resolveMatchFunctions = (subjectToMatch, functions, scope) => { 8 | for (let i = 0; i < functions.length; i++) { 9 | const currentMatch = getMatchDetails.call(scope, functions[i]) 10 | 11 | const matchHasSingleArgument = currentMatch.args.length === 1 12 | if (matchHasSingleArgument) { 13 | const singleValueResolve = match(currentMatch, subjectToMatch) 14 | if (singleValueResolve.hasValue) { 15 | return currentMatch.func(singleValueResolve.value) 16 | } 17 | } 18 | 19 | const matchHasMultipleArguments = currentMatch.args.length > 1 20 | if (matchHasMultipleArguments && Array.isArray(subjectToMatch)) { 21 | const multipleItemResolve = matchArray(currentMatch, subjectToMatch) 22 | if ( 23 | multipleItemResolve.hasValue && 24 | Array.isArray(multipleItemResolve.value) 25 | ) { 26 | return currentMatch.func.apply(null, multipleItemResolve.value) 27 | } 28 | 29 | if (multipleItemResolve.hasValue) { 30 | return currentMatch.func(multipleItemResolve.value) 31 | } 32 | } 33 | 34 | if ( 35 | hasDestructuredObjectArguments(currentMatch.args) && 36 | objectAndArgsDestructureMatches(currentMatch.args, subjectToMatch) 37 | ) { 38 | return currentMatch.func(subjectToMatch) 39 | } 40 | } 41 | } 42 | 43 | const matches = (subjectToMatch) => function (...functions) { 44 | return resolveMatchFunctions(subjectToMatch, functions, this) 45 | } 46 | 47 | module.exports = { matches } 48 | -------------------------------------------------------------------------------- /tests/arrays.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { matches } = require('src/z') 3 | 4 | describe('arrays', () => { 5 | it('should match tail array with comparsion at tail argument', () => { 6 | const result = matches([1, 2, 3])( 7 | (x, xs = [1, 2]) => false, 8 | (x, xs = [2, 3]) => true, 9 | (x, xs) => false 10 | ) 11 | result.should.equal(true) 12 | }) 13 | 14 | it('should match head array with comparsion at head argument', () => { 15 | const result = matches([1, 2, 3])( 16 | (x, y = 1, xs) => false, 17 | (x, y = 2, xs) => true, 18 | (x, y, xs) => false 19 | ) 20 | 21 | result.should.equal(true) 22 | }) 23 | 24 | it('should match single item array with comparsion', () => { 25 | const result = matches([1])( 26 | (x = [2]) => false, 27 | (x = [1]) => x 28 | ) 29 | 30 | result.should.deep.equal([1]) 31 | }) 32 | 33 | it('should match tail array', () => { 34 | const result = matches([1, 2, 3, 4])( 35 | (x, y, xs) => [x].concat(xs) 36 | ) 37 | 38 | result.should.deep.equal([1, 3, 4]) 39 | }) 40 | 41 | it('should match if even with match has more arguments than subject', () => { 42 | const result = matches([1])( 43 | (x, y, xs) => false, 44 | x => x 45 | ) 46 | 47 | result.should.deep.equal([1]) 48 | }) 49 | 50 | it('should match on unspecified head with tail argument', () => { 51 | const result = matches([1])( 52 | (x, y, xs, zs) => false, 53 | (x = 88, xs = []) => 11, 54 | (x, xs = []) => 10 55 | ) 56 | 57 | result.should.equal(10) 58 | }) 59 | 60 | it('should properly null elemnt on array when its not present', () => { 61 | const result = matches([1, 2, 3])( 62 | (x, y, xs, zs) => false, 63 | (x = 1, y = null, z = 3, xs = []) => 11, 64 | (x, xs) => 10 65 | ) 66 | 67 | result.should.equal(10) 68 | }) 69 | 70 | it('should properly null elemnt on array when its present', () => { 71 | const result = matches([1, null, 3])( 72 | (x, y, xs, zs) => false, 73 | (x = 1, y = null, zs) => 11, 74 | (x, xs) => 10 75 | ) 76 | 77 | result.should.equal(11) 78 | }) 79 | 80 | it('should extract array from head when has tail argument', () => { 81 | const result = matches([1])( 82 | (x, y, xs) => false, 83 | (x, xs = []) => x 84 | ) 85 | 86 | result.should.equal(1) 87 | }) 88 | 89 | it('should extract head value of array', () => { 90 | const result = matches([1, 2])( 91 | (x = 1, xs) => x 92 | ) 93 | 94 | result.should.equal(1) 95 | }) 96 | 97 | it('should match array of array on head', () => { 98 | const result = matches([[1], [2]])( 99 | (x = Array, xs) => xs 100 | ) 101 | 102 | result.should.deep.equal([[2]]) 103 | }) 104 | 105 | it('should match array of array on tail', () => { 106 | const result = matches([[1], [2]])( 107 | (x, xs = Array) => xs 108 | ) 109 | 110 | result.should.deep.equal([[2]]) 111 | }) 112 | 113 | it('should match empty array of on head', () => { 114 | const result = matches([[], [2]])( 115 | (x = [], xs) => xs 116 | ) 117 | 118 | result.should.deep.equal([[2]]) 119 | }) 120 | 121 | it('should match empty array of on tail', () => { 122 | const result = matches([1, []])( 123 | (x, xs = [[]]) => xs 124 | ) 125 | 126 | result.should.deep.equal([[]]) 127 | }) 128 | }) 129 | -------------------------------------------------------------------------------- /tests/matchObject.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { 3 | getFlattenedKeysFromArgs, 4 | objectAndArgsDestructureMatches 5 | } = require('../src/matchObject') 6 | 7 | // declared as a single string for readability here 8 | // but the reflection lib will hand it over as the 9 | // string split on commas and trimmed 10 | const args = '{a,b:{c,d,e:{f},g}}' 11 | const parsedArgs = args.split(',') 12 | 13 | describe('matchObject', () => { 14 | describe('#getFlattenedKeysFromArgs', () => { 15 | it('should get a list of required paths', () => { 16 | getFlattenedKeysFromArgs(parsedArgs).should.deep.equal([ 17 | 'a', 18 | 'b.c', 19 | 'b.d', 20 | 'b.e.f', 21 | 'b.g' 22 | ]) 23 | }) 24 | }) 25 | 26 | describe('#objectAndArgsDestructureMatches', () => { 27 | it('should match an object with extra keys to a spec with less', () => { 28 | objectAndArgsDestructureMatches(parsedArgs, { 29 | a: 1, 30 | b: { c: 1, d: 1, e: { f: 1 }, g: 1 } 31 | }).should.equal(true) 32 | }) 33 | 34 | it('should not match an object with missing keys', () => { 35 | objectAndArgsDestructureMatches(parsedArgs, { a: 1 }).should.equal(false) 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /tests/matches.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { matches } = require('src/z') 3 | 4 | describe('matches', () => { 5 | it('should match tail array with comparsion at tail argument', () => { 6 | const result = matches([1, 2, 3])( 7 | (x, xs = [1, 2]) => false, 8 | (x, xs = [2, 3]) => true, 9 | (x, xs) => false 10 | ) 11 | result.should.equal(true) 12 | }) 13 | 14 | it('should match head array with comparsion at head argument', () => { 15 | const result = matches([1, 2, 3])( 16 | (x, y = 1, xs) => false, 17 | (x, y = 2, xs) => true, 18 | (x, y, xs) => false 19 | ) 20 | 21 | result.should.equal(true) 22 | }) 23 | 24 | it('should match single item', () => { 25 | const result = matches(1)(x => true, () => false) 26 | 27 | result.should.equal(true) 28 | }) 29 | 30 | it('should match single item even when correct match is after wrong match', () => { 31 | const result = matches('test')( 32 | () => false, 33 | (x) => true, 34 | (x) => false 35 | ) 36 | 37 | result.should.equal(true) 38 | }) 39 | 40 | it('should execute function when matches occurs', () => { 41 | const result = matches(1)( 42 | (x) => 1 + x 43 | ) 44 | 45 | result.should.equal(2) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /tests/objectEquals.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const objectEquals = require('src/objectEquals') 3 | 4 | describe('objectEquals', () => { 5 | it('should match nested objects A', () => { 6 | const objectA = { 7 | name: 'oi', 8 | data: { 9 | age: 17 10 | } 11 | } 12 | 13 | const objectB = { 14 | name: 'oi', 15 | data: { 16 | age: 18 17 | } 18 | } 19 | 20 | objectEquals(objectA, objectB).should.equal(false) 21 | }) 22 | 23 | it('should match nested objects B', () => { 24 | const objectA = { 25 | data: { 26 | age: 18 27 | } 28 | } 29 | 30 | const objectB = { 31 | name: 'oi', 32 | data: { 33 | age: 18 34 | } 35 | } 36 | 37 | objectEquals(objectA, objectB).should.equal(true) 38 | }) 39 | 40 | it('should match nested objects C', () => { 41 | const objectA = { 42 | data: { 43 | id: { 44 | security: '123' 45 | } 46 | } 47 | } 48 | 49 | const objectB = { 50 | name: 'oi', 51 | data: { 52 | age: 18, 53 | id: { 54 | security: '123' 55 | } 56 | } 57 | } 58 | 59 | objectEquals(objectA, objectB).should.equal(true) 60 | }) 61 | 62 | it('should match nested objects D', () => { 63 | const objectA = { 64 | data: { 65 | id: { 66 | security: ['123', 3] 67 | } 68 | } 69 | } 70 | 71 | const objectB = { 72 | name: 'oi', 73 | data: { 74 | age: 18, 75 | id: { 76 | security: ['123', 2] 77 | } 78 | } 79 | } 80 | 81 | objectEquals(objectA, objectB).should.equal(false) 82 | }) 83 | 84 | it('should match nested objects E', () => { 85 | const objectA = { 86 | data: { 87 | id: { 88 | security: ['123', 2] 89 | } 90 | } 91 | } 92 | 93 | const objectB = { 94 | name: 'oi', 95 | data: { 96 | age: 18, 97 | id: { 98 | security: ['123', 2] 99 | } 100 | } 101 | } 102 | 103 | objectEquals(objectA, objectB).should.equal(true) 104 | }) 105 | }) 106 | -------------------------------------------------------------------------------- /tests/objects.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { matches } = require('src/z') 3 | 4 | describe('destructured object matching', () => { 5 | it('should match on presence of keys in an object', () => { 6 | const arg = { x: 1, y: 1 } 7 | const result = matches(arg)( 8 | ({ x }) => true, 9 | ([a, b]) => false, x => 'default' 10 | ) 11 | 12 | result.should.equal(true) 13 | }) 14 | 15 | it('should not match an object with missing keys', () => { 16 | const arg = { x: 1, y: 1 } 17 | const result = matches(arg)( 18 | ({ x, z }) => x + z, 19 | ([a, b]) => false, x => 'default' 20 | ) 21 | 22 | result.should.equal('default') 23 | }) 24 | 25 | it('should pass object keys through to match function', () => { 26 | const arg = { x: 1, y: 1 } 27 | const result = matches(arg)( 28 | ({ x, y }) => x + y, 29 | ([a, b]) => false, x => 'default' 30 | ) 31 | 32 | result.should.equal(2) 33 | }) 34 | 35 | it('should match exact object value', () => { 36 | const result = matches({ name: 'Maria' })( 37 | (x = { name: 'John' }) => false, 38 | (x = { name: 'Maria' }) => true, 39 | (x) => false 40 | ) 41 | 42 | result.should.be.true 43 | }) 44 | 45 | it('should match something contained in a object', () => { 46 | const result = matches({ name: 'Maria', data: {age: 18} })( 47 | (x = { age: 17 }) => false, 48 | (x = { age: 18 }) => false, 49 | (x = { name: 'Maria' }) => true 50 | ) 51 | 52 | result.should.be.true 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /tests/outsideScope.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { matches } = require('src/z') 3 | 4 | describe('outside scope matching', () => { 5 | it('should match variable outside scope', () => { 6 | const FILTERS = { 7 | CATEGORY: 'CATEGORY', 8 | PRICE: 'PRICE', 9 | COLOR: 'COLOR', 10 | ONLY_NEW: 'ONLY_NEW', 11 | ONLY_IN_DISCOUNT: 'ONLY_IN_DISCOUNT' 12 | } 13 | 14 | this.FILTERS = FILTERS 15 | const someValue = FILTERS.COLOR 16 | 17 | const result = matches(someValue).call(this, 18 | (x = FILTERS.CATEGORY) => false, 19 | (x = FILTERS.COLOR) => true, 20 | (x = FILTERS.ONLY_NEW) => false 21 | ) 22 | 23 | result.should.equal(true) 24 | }) 25 | 26 | it('should match variable outside scope 2', () => { 27 | const innerObject = { 28 | FILTERS: { 29 | CATEGORY: 'CATEGORY', 30 | PRICE: 'PRICE', 31 | COLOR: 'COLOR', 32 | ONLY_NEW: 'ONLY_NEW', 33 | ONLY_IN_DISCOUNT: 'ONLY_IN_DISCOUNT' 34 | } 35 | } 36 | 37 | const someValue = innerObject.FILTERS.COLOR 38 | 39 | const result = matches(someValue).call({ innerObject }, 40 | (x = innerObject.FILTERS.CATEGORY) => false, 41 | (x = innerObject.FILTERS.COLOR) => true, 42 | (x = innerObject.FILTERS.ONLY_NEW) => false 43 | ) 44 | 45 | result.should.equal(true) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /tests/regex.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { matches } = require('src/z') 3 | 4 | describe('regex', () => { 5 | it('should match item if regex tests true', () => { 6 | const result = matches('HIPA')( 7 | (x = /[PA]/) => true, 8 | (x) => false 9 | ) 10 | result.should.equal(true) 11 | }) 12 | 13 | it('should not match item if regex tests false', () => { 14 | const result = matches('FOO')( 15 | (x = /[PA]/) => true, 16 | (x) => false 17 | ) 18 | 19 | result.should.equal(false) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /tests/types.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { matches } = require('src/z') 3 | 4 | describe('types', () => { 5 | it('should match exaclty string', () => { 6 | const result = matches('string')( 7 | (x = 'stringo') => false, 8 | (x = 'string') => true, 9 | (x = 'stringi') => false 10 | ) 11 | 12 | result.should.equal(true) 13 | }) 14 | 15 | it('should match string type', () => { 16 | const result = matches('string')( 17 | (x = String) => true 18 | ) 19 | 20 | result.should.equal(true) 21 | }) 22 | 23 | it('should match bool type', () => { 24 | const result = matches(true)( 25 | (x = Boolean) => true 26 | ) 27 | 28 | result.should.equal(true) 29 | }) 30 | 31 | it('should match number type', () => { 32 | const result = matches(1)( 33 | (x = Boolean) => false, 34 | (x = Number) => true, 35 | (x = Number) => false 36 | ) 37 | 38 | result.should.equal(true) 39 | }) 40 | 41 | it('should match object type', () => { 42 | const result = matches({ a: 1 })( 43 | (x = Object) => true, 44 | (x = Object) => false 45 | ) 46 | 47 | result.should.equal(true) 48 | }) 49 | 50 | it('should match instance', () => { 51 | const result = matches(new Date())( 52 | (x = Date) => true 53 | ) 54 | 55 | result.should.equal(true) 56 | }) 57 | 58 | it('should match single item array', () => { 59 | const result = matches([1])( 60 | (x = [1]) => x 61 | ) 62 | 63 | result.should.deep.equal([1]) 64 | }) 65 | 66 | it('should match array', () => { 67 | const result = matches([1])( 68 | (x = Array) => x 69 | ) 70 | 71 | result.should.deep.equal([1]) 72 | }) 73 | 74 | it('should match null', () => { 75 | const result = matches(null)( 76 | (x = undefined) => false, 77 | (x = null) => true, 78 | (x = undefined) => false 79 | ) 80 | 81 | result.should.equal(true) 82 | }) 83 | 84 | it('should match undefined', () => { 85 | const result = matches(undefined)( 86 | (x = null) => false, 87 | (x = undefined) => true, 88 | (x = null) => false 89 | ) 90 | 91 | result.should.equal(true) 92 | }) 93 | 94 | it('should match object', () => { 95 | const result = matches({ x: 3 })( 96 | (x = String) => `${x} is a String`, 97 | (x = Object) => 'Object' 98 | ) 99 | 100 | result.should.equal('Object') 101 | }) 102 | }) 103 | -------------------------------------------------------------------------------- /tests/usages.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { matches } = require('src/z') 3 | 4 | describe('old tests', function () { 5 | it('should map an array', function () { 6 | var $map = (numbers, f) => { 7 | return matches(numbers)( 8 | (_, xs = []) => [], 9 | (x, xs) => [f(x)].concat(xs.map(f)) 10 | ) 11 | } 12 | 13 | $map([1, 2, 3, 4, 5], number => number * 2).should.eql([2, 4, 6, 8, 10]) 14 | }) 15 | 16 | it('should use 3 positions of pattern matching', function () { 17 | var compress = numbers => 18 | matches(numbers)( 19 | (x, y, xs) => x === y ? compress([x].concat(xs)) : [x].concat(compress([y].concat(xs))), 20 | (x, xs) => x // stopping condition 21 | ) 22 | 23 | compress([1, 1, 2, 3, 3, 3]).should.eql([1, 2, 3]) 24 | }) 25 | 26 | it('should map a constant', function () { 27 | matches('h')( 28 | (x = 'h') => true, 29 | (x) => false 30 | ).should.equal(true) 31 | }) 32 | 33 | it('should map a constant 2', function () { 34 | matches('a')( 35 | (x = 'h') => true, 36 | (x) => false 37 | ).should.equal(false) 38 | }) 39 | 40 | it('should map a constant 3', function () { 41 | var factorial = function (number) { 42 | return matches(number)( 43 | function (x) { 44 | return (x === 0) ? 1 : x * factorial(x - 1) 45 | } 46 | ) 47 | } 48 | }) 49 | 50 | it('should reverse a list', function () { 51 | const myReverse = list => 52 | matches(list)( 53 | (head, tail = []) => [head], 54 | (head, tail) => myReverse(tail).concat(head) 55 | ) 56 | 57 | myReverse([1, 2, 3, 4, 5]).should.eql([5, 4, 3, 2, 1]) 58 | }) 59 | 60 | it('should reverse a list with function', function () { 61 | const myReverse = list => 62 | matches(list)( 63 | function (head, tail = []) { return [head] }, 64 | function (head, tail) { return myReverse(tail).concat(head) } 65 | ) 66 | 67 | myReverse([1, 2, 3, 4, 5]).should.eql([5, 4, 3, 2, 1]) 68 | }) 69 | 70 | it('should match array of arrays', function () { 71 | var matched = false 72 | 73 | matches([1, 2, [3]])( 74 | (x = Array) => { 75 | (matched = true) 76 | }, 77 | (x) => { 78 | console.log('here', x) 79 | } 80 | ) 81 | 82 | matched.should.eql(true) 83 | }) 84 | 85 | it('should match a number', function () { 86 | matches(1)( 87 | (x) => true 88 | ).should.equal(true) 89 | }) 90 | 91 | it('should match a string', function () { 92 | matches('test')( 93 | (x = 'testa') => false, 94 | (x = 'test') => true, 95 | (x = 'testo') => false, 96 | function otherwise () { 97 | return false 98 | } 99 | ).should.equal(true) 100 | }) 101 | }) 102 | -------------------------------------------------------------------------------- /tests/valueComparsion.spec.js: -------------------------------------------------------------------------------- 1 | require('chai').should() 2 | const { matches } = require('src/z') 3 | 4 | describe('value comparsion', () => { 5 | it('should match single item with value comparison', () => { 6 | const result = matches(1)((x = 2) => false, (x = 1) => true, (x = 3) => false) 7 | 8 | result.should.equal(true) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /z-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-pattern-matching/z/39c35d920e546d562634c6755d772d95afeeeba9/z-logo.png --------------------------------------------------------------------------------