├── .gitignore ├── README2.md ├── .babelrc ├── package.json ├── LICENSE ├── src └── atcon.js ├── index.js ├── README.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /README2.md: -------------------------------------------------------------------------------- 1 | # atcon 2 | 3 | TODO English README.md -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-runtime", "add-module-exports"] 4 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "atcon", 3 | "version": "1.0.4", 4 | "description": "atom conditions", 5 | "main": "index.js", 6 | "scripts": { 7 | "compile": "babel src/atcon.js -o index.js", 8 | "test": "mocha test.js", 9 | "prepublish": "npm run compile && npm run test" 10 | }, 11 | "keywords": [ 12 | "atcon", 13 | "atom", 14 | "conditions" 15 | ], 16 | "author": "Xaber", 17 | "license": "MIT", 18 | "dependencies": { 19 | "babel-runtime": "6.9.0" 20 | }, 21 | "devDependencies": { 22 | "babel-cli": "^6.23.0", 23 | "babel-plugin-add-module-exports": "^0.2.1", 24 | "babel-plugin-transform-runtime": "^6.23.0", 25 | "babel-preset-es2015": "^6.22.0", 26 | "chai": "^3.5.0", 27 | "mocha": "^3.2.0" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/Xaber20110202/atcon.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/Xaber20110202/atcon/issues" 35 | }, 36 | "homepage": "https://github.com/Xaber20110202/atcon#readme" 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Xaber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/atcon.js: -------------------------------------------------------------------------------- 1 | const atcon = (() => { 2 | 3 | const breaker = {}; 4 | const isUndefined = obj => obj === void 0; 5 | 6 | // iterator can receive breaker to return and then this reduce will be breaked 7 | // and it also receive values, for the situation that we need look back upon 8 | const reduce = (array, iterator, initialValue) => { 9 | let value = initialValue; 10 | // upper levels 11 | let values = []; 12 | 13 | array.some((v) => { 14 | values.push(value); 15 | 16 | // inspired by Underscore.js 1.6.0 17 | value = iterator(value, v, breaker, values); 18 | if (value === breaker) { 19 | return true; 20 | } 21 | }); 22 | }; 23 | 24 | const atcon = (conditions, states, predicate) => { 25 | reduce(states, (conditions, state, breaker, preConditions) => { 26 | // our states don't have corresponding value 27 | // then we can get the default value 28 | if (isUndefined(conditions) || isUndefined(conditions[state])) { 29 | preConditions.reverse().some((condition) => { 30 | // use predicate to break some func 31 | return predicate(condition && condition.__DEFAULT__); 32 | }); 33 | return breaker; 34 | 35 | } else { 36 | return predicate(conditions[state]) ? breaker : conditions[state]; 37 | } 38 | 39 | }, conditions); 40 | }; 41 | 42 | // predicate can receive conditions item by states and 43 | // the result is judged by the predicate returned true. 44 | // if you return true, you will quit the iteration 45 | // if you don't return true, you will only get undefined 46 | return (conditions, states, predicate) => { 47 | let result; 48 | atcon(conditions, states, (item) => { 49 | if (predicate(item) === true) { 50 | result = item; 51 | return true; 52 | } 53 | }); 54 | return result; 55 | }; 56 | })(); 57 | 58 | export default atcon; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var atcon = function () { 7 | 8 | var breaker = {}; 9 | var isUndefined = function isUndefined(obj) { 10 | return obj === void 0; 11 | }; 12 | 13 | // iterator can receive breaker to return and then this reduce will be breaked 14 | // and it also receive values, for the situation that we need look back upon 15 | var reduce = function reduce(array, iterator, initialValue) { 16 | var value = initialValue; 17 | // upper levels 18 | var values = []; 19 | 20 | array.some(function (v) { 21 | values.push(value); 22 | 23 | // inspired by Underscore.js 1.6.0 24 | value = iterator(value, v, breaker, values); 25 | if (value === breaker) { 26 | return true; 27 | } 28 | }); 29 | }; 30 | 31 | var atcon = function atcon(conditions, states, predicate) { 32 | reduce(states, function (conditions, state, breaker, preConditions) { 33 | // our states don't have corresponding value 34 | // then we can get the default value 35 | if (isUndefined(conditions) || isUndefined(conditions[state])) { 36 | preConditions.reverse().some(function (condition) { 37 | // use predicate to break some func 38 | return predicate(condition && condition.__DEFAULT__); 39 | }); 40 | return breaker; 41 | } else { 42 | return predicate(conditions[state]) ? breaker : conditions[state]; 43 | } 44 | }, conditions); 45 | }; 46 | 47 | // predicate can receive conditions item by states and 48 | // the result is judged by the predicate returned true. 49 | // if you return true, you will quit the iteration 50 | // if you don't return true, you will only get undefined 51 | return function (conditions, states, predicate) { 52 | var result = void 0; 53 | atcon(conditions, states, function (item) { 54 | if (predicate(item) === true) { 55 | result = item; 56 | return true; 57 | } 58 | }); 59 | return result; 60 | }; 61 | }(); 62 | 63 | exports.default = atcon; 64 | module.exports = exports["default"]; 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # atcon 2 | 3 | ### atcon是什么 4 | 1. con即`condition`条件,atcon,指为条件而生; 5 | 2. at也来源atom,代表它是一个原子操作; 6 | 3. 把复杂的`if else`逻辑转变为简单的原子操作,就在atcon。 7 | 8 | ### atcon的目标 9 | 和复杂的 `if else` 说再见 10 | 11 | ### 为什么 12 | 13 | 具体见[使用atcon告别混乱的if else](http://xaber.co/2017/03/02/%E4%BD%BF%E7%94%A8atcon%E5%91%8A%E5%88%AB%E6%B7%B7%E4%B9%B1%E7%9A%84if-else/) 14 | 15 | ### 使用方式 16 | 17 | ``` 18 | npm install --save atcon 19 | const atcon = require('atcon'); 20 | atcon(conditions, states, predicate); 21 | ``` 22 | 23 | ### 执行逻辑 24 | 1. 根据`states`数组项元素,依次查找`condtions`对象(也可以是数组)的`state0`属性,得到`conditons1`对象,再查找`conditons1`的`state1`属性...... 其实相当于一个`reduce` 25 | 2. `predicate`接收`reduce`传进来的每一项的`conditon[state]`,如果满足条件,`predicate`函数 `return true` 就退出查找,得到该值 26 | 3. 如果`conditon[state]`不存在,则重新回到上层查找,层层回溯,并获取该层对象的`__DEFAULT__`属性,传递给`predicate`,同样的,如果`return true`,退出查找,得到该值。其实相当于 `switch`内的`default` 27 | 28 | ### 具体例子 29 | ``` 30 | const imgMap = { 31 | online: { 32 | '2': { 33 | a: 'img_b', 34 | b: 'img_o' 35 | }, 36 | '3': { 37 | a: 'img_b', 38 | b: 'img_p' 39 | }, 40 | '4': 'img_c', 41 | '5': 'img_d', 42 | '6': 'img_e' 43 | }, 44 | offline: { 45 | '2': 'img_h', 46 | '3': 'img_i', 47 | '4': 'img_j', 48 | '5': 'img_k', 49 | '6': 'img_l' 50 | }, 51 | __DEFAULT__: 'img_a' 52 | }; 53 | 54 | const noticeMap = { 55 | b: { 56 | '3': 'text3', 57 | '5': 'text5' 58 | }, 59 | a: 'textaaa', 60 | __DEFAULT__: 'textdefault' 61 | }; 62 | 63 | const isString = obj => Object.prototype.toString.call(obj) === '[object String]'; 64 | 65 | atcon(imgMap, ['online', 3, 'a'], isString); // 'img_b' 66 | atcon(imgMap, ['online', 3, 'c'], isString); // 'img_a' 67 | atcon(imgMap, ['offline', 3, 'v'], isString); // 'img_i' 68 | atcon(imgMap, ['noline'], isString); // 'img_a' 69 | 70 | atcon(noticeMap, ['b', 1], isString); // 'textdefault' 71 | atcon(noticeMap, ['a', 6, 1, 5, 6], isString); // 'textaaa' 72 | ``` 73 | 74 | **注意** 75 | ``` 76 | atcon(noticeMap, ['b'], isString); // undefined 77 | ``` 78 | 返回的是 `undefined`,因为走进了 `switch case b`的逻辑,但是`switch case b`是一个对象,没有满足isString的条件,而这里没有指定下一层状态的话,循环就会在这一层戛然而止,而不再做回溯。 79 | 80 | 更多例子可直接参考[mocha test](https://github.com/Xaber20110202/atcon/blob/master/test.js) 81 | 82 | ### 最后 83 | 希望大家用得开心。 84 | 85 | ### License 86 | 87 | MIT -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var atcon = require('./index.js'); 3 | 4 | var codeArr = [ 5 | '0', [ 6 | [ 7 | '100', 8 | '101', 9 | ], 10 | '11', [ 11 | '120', 12 | '121', 13 | ], 14 | [ 15 | '130', 16 | '131', 17 | ], 18 | [ 19 | '140', 20 | '141' 21 | ] 22 | ] 23 | ]; 24 | 25 | var resultMap = { 26 | a: { 27 | b: { 28 | a: 6, 29 | c: 10 30 | }, 31 | __DEFAULT__: -100 32 | }, 33 | b: { 34 | x: { 35 | a: 10, 36 | b: 20, 37 | c: { 38 | d: 70, 39 | __DEFAULT__: 60 40 | } 41 | }, 42 | __DEFAULT__: 30 43 | }, 44 | __DEFAULT__: 1000 45 | }; 46 | 47 | var imgMap = { 48 | online: { 49 | '2': { 50 | a: 'img_b', 51 | b: 'img_o' 52 | }, 53 | '3': { 54 | a: 'img_b', 55 | b: 'img_p' 56 | }, 57 | '4': 'img_c', 58 | '5': 'img_d', 59 | '6': 'img_e' 60 | }, 61 | offline: { 62 | '2': 'img_h', 63 | '3': 'img_i', 64 | '4': 'img_j', 65 | '5': 'img_k', 66 | '6': 'img_l' 67 | }, 68 | __DEFAULT__: 'img_a' 69 | }; 70 | 71 | var noticeMap = { 72 | b: { 73 | '3': '文案三', 74 | '5': '文案五' 75 | }, 76 | a: '一样的ABTEST文案', 77 | __DEFAULT__: '文案零' 78 | }; 79 | 80 | var isString = obj => Object.prototype.toString.call(obj) === '[object String]'; 81 | var isNumber = obj => Object.prototype.toString.call(obj) === '[object Number]'; 82 | 83 | var tests = [ 84 | { 85 | name: 'test 11', 86 | params: [codeArr, [1, 1, 1], isString], 87 | expect: '11' 88 | }, { 89 | name: 'test 101', 90 | params: [codeArr, [1, 0, 1], isString], 91 | expect: '101' 92 | }, { 93 | name: 'test 001', 94 | params: [codeArr, [0, 0, 1], isString], 95 | expect: '0' 96 | }, { 97 | name: 'test aba', 98 | params: [resultMap, ['a', 'b', 'a'], isNumber], 99 | expect: 6 100 | }, 101 | { 102 | name: 'test abdc', 103 | params: [resultMap, ['a', 'b', 'd', 'c'], isNumber], 104 | expect: -100 105 | }, { 106 | name: 'test b', 107 | params: [resultMap, ['b'], isNumber], 108 | expect: undefined 109 | }, { 110 | name: 'test bxcd', 111 | params: [resultMap, ['b', 'x', 'c', 'd'], isNumber], 112 | expect: 70 113 | }, 114 | { 115 | name: 'test bxcde', 116 | params: [resultMap, ['b', 'x', 'c', 'd', 'e'], isNumber], 117 | expect: 70 118 | }, 119 | { 120 | name: 'test bxc', 121 | params: [resultMap, ['b', 'x', 'c'], isNumber], 122 | expect: undefined 123 | }, 124 | { 125 | name: 'test bxcf', 126 | params: [resultMap, ['b', 'x', 'c', 'f'], isNumber], 127 | expect: 60 128 | }, 129 | { 130 | name: 'test bxff', 131 | params: [resultMap, ['b', 'x', 'f', 'f'], isNumber], 132 | expect: 30 133 | }, 134 | { 135 | name: 'test x', 136 | params: [resultMap, ['x'], isNumber], 137 | expect: 1000 138 | }, 139 | { 140 | name: 'test imgMap online 3 a', 141 | params: [imgMap, ['online', 3, 'a'], isString], 142 | expect: 'img_b' 143 | }, 144 | { 145 | name: 'test imgMap online 3 c', 146 | params: [imgMap, ['online', 3, 'c'], isString], 147 | expect: 'img_a' 148 | }, 149 | { 150 | name: 'test imgMap offline 3 b', 151 | params: [imgMap, ['offline', 3, 'v'], isString], 152 | expect: 'img_i' 153 | }, 154 | { 155 | name: 'test imgMap noline', 156 | params: [imgMap, ['noline'], isString], 157 | expect: 'img_a' 158 | }, 159 | { 160 | name: 'test noticeMap b 1', 161 | params: [noticeMap, ['b', 1], isString], 162 | expect: '文案零' 163 | }, 164 | { 165 | name: 'test noticeMap a 6 1 5 6', 166 | params: [noticeMap, ['a', 6, 1, 5, 6], isString], 167 | expect: '一样的ABTEST文案' 168 | }]; 169 | 170 | describe('test atcon', () => { 171 | tests.forEach((test) => { 172 | it(test.name, () => { 173 | expect(atcon.apply(null, test.params)).to.be.equal(test.expect); 174 | }); 175 | }); 176 | }); --------------------------------------------------------------------------------