├── .editorconfig ├── .eslintrc ├── .gitignore ├── .travis.yml ├── README.md ├── collections ├── README.md ├── collection-operator │ ├── collection-operator.js │ └── collection-operator.test.js ├── filter │ ├── filter.js │ └── filter.test.js ├── flatten │ ├── flatten.js │ └── flatten.test.js ├── map │ ├── map.js │ └── map.test.js ├── reduce │ ├── reduce.js │ └── reduce.test.js ├── section │ ├── section.js │ └── section.test.js └── super-position-operation │ ├── collection-operation │ ├── collection-operation.js │ └── collection-operation.test.js │ ├── interval-operation │ ├── interval-operation.js │ └── interval-operation.test.js │ └── own-elements-operation │ ├── own-elements-operation.js │ └── own-elements-operation.test.js ├── es6 └── .gitkeep ├── jest.setup.js ├── lodash ├── comparison │ ├── diffs.js │ └── diffs.test.js ├── count-elements │ ├── index.js │ └── index.test.js └── group │ ├── apply-calculation-to-group.js │ └── apply-calculation-to-group.test.js ├── package-lock.json ├── package.json ├── refactor └── word-frequency │ ├── README.md │ ├── index.js │ └── index.test.js ├── scripts └── .gitkeep └── tdd ├── anagrams ├── README.md ├── index.js └── index.test.js ├── args ├── README.md ├── args-parser │ ├── index.js │ └── index.test.js ├── args │ └── index.js └── schema │ └── index.js ├── bowling ├── README.md ├── index.js └── index.test.js ├── fizzbuzz ├── README.md ├── index.js └── index.test.js ├── guess-number └── README.md ├── lcd ├── README.md ├── index.js └── index.test.js ├── pos-v1 ├── README.md ├── data │ ├── items.js │ └── promotions.js ├── index.js └── index.test.js └── take-out-food ├── README.md ├── data ├── items.js └── promotions.js ├── index.js └── index.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "plugins": ["prettier"], 4 | "extends": [ 5 | "prettier", 6 | "eslint:recommended" 7 | ], 8 | "env": { 9 | "jest": true, 10 | "node": true, 11 | "es6": true 12 | }, 13 | "globals":{ 14 | "__DEV__": true 15 | }, 16 | "rules": { 17 | "prettier/prettier": "error" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | coverage 4 | TODOLIST.md 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'node' 4 | install: 5 | - npm ci 6 | script: 7 | - npm run lint 8 | - npm run test:ci 9 | cache: 10 | directories: 11 | - 'node_modules' 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 刻意练习 [![][badges: travis ci]][links: travis ci] 2 | 3 | Credits to: 4 | 5 | - [tws-practice/collection-calculate-camp][] 6 | - [es6kata][] 7 | 8 | [tws-practice/collection-calculate-camp]: https://github.com/tws-practice/collection-calculate-camp 9 | [es6kata]: http://es6katas.org 10 | [badges: travis ci]: https://img.shields.io/travis/linesh-simplicity/frontend-practices.svg?branch=master 11 | [links: travis ci]: https://travis-ci.org/linesh-simplicity/frontend-practices 12 | -------------------------------------------------------------------------------- /collections/README.md: -------------------------------------------------------------------------------- 1 | ## JS 进阶集合练习 2 | 3 | 运行所有测试: 4 | 5 | ```bash 6 | npm test 7 | ``` 8 | -------------------------------------------------------------------------------- /collections/collection-operator/collection-operator.js: -------------------------------------------------------------------------------- 1 | export const collectAllEven = (collection) => { 2 | return [collection] 3 | } 4 | 5 | export const collectLastElement = (collection) => { 6 | return [collection] 7 | } 8 | 9 | export const getIntegerInterval = (number_a, number_b) => { 10 | return [number_a, number_b] 11 | } 12 | 13 | export const getEvenIntegerInterval = (number_a, number_b) => { 14 | return [number_a, number_b] 15 | } 16 | 17 | export const getIntersection = (collection_a, collection_b) => { 18 | return [collection_a, collection_b] 19 | } 20 | 21 | export const getLetterInterval = (number_a, number_b) => { 22 | return [number_a, number_b] 23 | } 24 | 25 | export const getLetterInterval2 = (number_a, number_b) => { 26 | return [number_a, number_b] 27 | } 28 | 29 | export const getUnion = (collection_a, collection_b) => { 30 | return [collection_a, collection_b] 31 | } 32 | -------------------------------------------------------------------------------- /collections/collection-operator/collection-operator.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | collectLastElement, 3 | collectAllEven, 4 | getIntegerInterval, 5 | getEvenIntegerInterval, 6 | getIntersection, 7 | getLetterInterval, 8 | getLetterInterval2, 9 | getUnion, 10 | } from './collection-operator' 11 | 12 | describe('collectAllEven', () => { 13 | const collection_a = [1, 2, 3, 4, 5] 14 | const collection_b = [2, 4] 15 | 16 | it.skip('选出给定区间中所有的偶数', () => { 17 | const result = collectAllEven(collection_a) 18 | expect(result).toEqual(collection_b) 19 | }) 20 | }) 21 | 22 | describe('collectLastElement', () => { 23 | const collection = [1, 2, 3, 4, 5] 24 | 25 | it.skip('弹出集合最后一个元素', () => { 26 | const result = collectLastElement(collection) 27 | expect(result).toEqual(5) 28 | }) 29 | }) 30 | 31 | describe('getIntegerInterval', () => { 32 | const collection_a = [1, 2, 3, 4, 5] 33 | const collection_b = [5, 4, 3, 2, 1] 34 | 35 | it.skip('根据给出的两个数字得到一个自增1的数字区间', () => { 36 | const result = getIntegerInterval(1, 5) 37 | expect(result).toEqual(collection_a) 38 | }) 39 | 40 | it.skip('根据给出的两个数字得到一个自减1的数字区间', () => { 41 | const result = getIntegerInterval(5, 1) 42 | expect(result).toEqual(collection_b) 43 | }) 44 | 45 | it.skip('给定两个相同的数字得到只含此数字的数组', () => { 46 | const result = getIntegerInterval(5, 5) 47 | expect(result).toEqual([5]) 48 | }) 49 | }) 50 | 51 | describe('getEvenIntegerInterval', () => { 52 | const collection_a = [2, 4, 6, 8, 10] 53 | const collection_b = [10, 8, 6, 4, 2] 54 | 55 | it.skip('根据给出的两个数字得到自增的偶数区间', () => { 56 | const result = getEvenIntegerInterval(1, 10) 57 | expect(result).toEqual(collection_a) 58 | }) 59 | 60 | it.skip('根据给出的两个数字得到自减的偶数区间', () => { 61 | const result = getEvenIntegerInterval(10, 1) 62 | expect(result).toEqual(collection_b) 63 | }) 64 | 65 | it.skip('给定两个相同的偶数得到只含此数字的数组', () => { 66 | const result = getEvenIntegerInterval(10, 10) 67 | expect(result).toEqual([10]) 68 | }) 69 | 70 | it.skip('给定两个相同的偶数得到只含此数字的数组', () => { 71 | const result = getEvenIntegerInterval(5, 5) 72 | expect(result).toEqual([]) 73 | }) 74 | }) 75 | 76 | describe('getIntersection', () => { 77 | const collection_a = [10, 27, 28, 19, 5] 78 | const collection_b = [5, 78, 28, 19, 23] 79 | const collection_c = [28, 19, 5] 80 | 81 | it.skip('弹出两个集合的交集', () => { 82 | const result = getIntersection(collection_a, collection_b) 83 | expect(result).toEqual(collection_c) 84 | }) 85 | }) 86 | 87 | describe('getLetterInterval', () => { 88 | const collection_a = ['a', 'b', 'c', 'd', 'e'] 89 | const collection_b = ['e', 'd', 'c', 'b', 'a'] 90 | 91 | it.skip('根据给出的两个数字得到对应自增的字母区间', () => { 92 | const result = getLetterInterval(1, 5) 93 | expect(result).toEqual(collection_a) 94 | }) 95 | 96 | it.skip('根据给出的两个数字得到对应自减的字母区间', () => { 97 | const result = getLetterInterval(5, 1) 98 | expect(result).toEqual(collection_b) 99 | }) 100 | 101 | it.skip('给定两个相同的数字得到只含此数字对应字母的数组', () => { 102 | const result = getLetterInterval(5, 5) 103 | expect(result).toEqual(['e']) 104 | }) 105 | }) 106 | 107 | describe('getLetterInterval2', () => { 108 | const collection_a = [ 109 | 't', 110 | 'u', 111 | 'v', 112 | 'w', 113 | 'x', 114 | 'y', 115 | 'z', 116 | 'aa', 117 | 'ab', 118 | 'ac', 119 | 'ad', 120 | 'ae', 121 | 'af', 122 | 'ag', 123 | 'ah', 124 | 'ai', 125 | 'aj', 126 | 'ak', 127 | 'al', 128 | 'am', 129 | 'an', 130 | 'ao', 131 | 'ap', 132 | 'aq', 133 | 'ar', 134 | 'as', 135 | 'at', 136 | 'au', 137 | 'av', 138 | 'aw', 139 | 'ax', 140 | 'ay', 141 | 'az', 142 | 'ba', 143 | ] 144 | const collection_b = [ 145 | 'ba', 146 | 'az', 147 | 'ay', 148 | 'ax', 149 | 'aw', 150 | 'av', 151 | 'au', 152 | 'at', 153 | 'as', 154 | 'ar', 155 | 'aq', 156 | 'ap', 157 | 'ao', 158 | 'an', 159 | 'am', 160 | 'al', 161 | 'ak', 162 | 'aj', 163 | 'ai', 164 | 'ah', 165 | 'ag', 166 | 'af', 167 | 'ae', 168 | 'ad', 169 | 'ac', 170 | 'ab', 171 | 'aa', 172 | 'z', 173 | 'y', 174 | 'x', 175 | 'w', 176 | 'v', 177 | 'u', 178 | 't', 179 | ] 180 | 181 | it.skip('根据给出的两个数字得到对应自增的字母区间', () => { 182 | const result = getLetterInterval2(20, 53) 183 | expect(result).toEqual(collection_a) 184 | }) 185 | 186 | it.skip('根据给出的两个数字得到对应自减的字母区间', () => { 187 | const result = getLetterInterval2(53, 20) 188 | expect(result).toEqual(collection_b) 189 | }) 190 | 191 | it.skip('给定两个相同的数字得到只含此数字对应字母的数组', () => { 192 | const result = getLetterInterval2(28, 28) 193 | expect(result).toEqual(['ab']) 194 | }) 195 | }) 196 | 197 | describe('getUnion', () => { 198 | const collection_a = [10, 27, 28, 19, 5] 199 | const collection_b = [5, 78, 28, 19, 23] 200 | const collection_c = [10, 27, 28, 19, 5, 78, 23] 201 | 202 | it.skip('弹出两个集合的并集', () => { 203 | const result = getUnion(collection_a, collection_b) 204 | expect(result).toEqual(collection_c) 205 | }) 206 | }) 207 | -------------------------------------------------------------------------------- /collections/filter/filter.js: -------------------------------------------------------------------------------- 1 | export const chooseEven = (collection) => { 2 | return [collection] 3 | } 4 | 5 | export const chooseMultiplesOfThree = (collection) => { 6 | return [collection] 7 | } 8 | 9 | export const chooseNoRepeatNumber = (collection) => { 10 | return [collection] 11 | } 12 | 13 | export const groupingCount = (collection) => { 14 | return [collection] 15 | } 16 | 17 | export const chooseCommonElements = (collection_a, collection_b) => { 18 | return [collection_a, collection_b] 19 | } 20 | 21 | export const chooseNoCommonElements = (collection_a, collection_b) => { 22 | return [collection_a, collection_b] 23 | } 24 | 25 | export const chooseDivisibleInteger = (collection_a, collection_b) => { 26 | return [collection_a, collection_b] 27 | } 28 | -------------------------------------------------------------------------------- /collections/filter/filter.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | chooseMultiplesOfThree, 3 | chooseEven, 4 | chooseNoRepeatNumber, 5 | groupingCount, 6 | chooseCommonElements, 7 | chooseNoCommonElements, 8 | chooseDivisibleInteger, 9 | } from './filter' 10 | 11 | describe('chooseEven', () => { 12 | const collection = [0, 1, 2, 3, 4, 6] 13 | 14 | it.skip('从collection中选出偶数', () => { 15 | const result = chooseEven(collection) 16 | 17 | expect(result).toEqual([0, 2, 4, 6]) 18 | }) 19 | }) 20 | 21 | describe('chooseMultiplesOfThree', () => { 22 | const collection = [0, 1, 2, 3, 4, 5, 6, 9, 11] 23 | 24 | it.skip('从collection中选出3的倍数', () => { 25 | const result = chooseMultiplesOfThree(collection) 26 | 27 | expect(result).toEqual([0, 3, 6, 9]) 28 | }) 29 | }) 30 | 31 | describe('chooseNoRepeatNumber', () => { 32 | const collection = [1, 1, 1, 2, 2, 3, 4] 33 | 34 | it.skip('从collection中选出不重复的数字', () => { 35 | const result = chooseNoRepeatNumber(collection) 36 | 37 | expect(result).toEqual([1, 2, 3, 4]) 38 | }) 39 | }) 40 | 41 | describe('groupingCount', () => { 42 | const collection = [1, 1, 1, 1, 2, 3, 1, 3, 4, 2, 3, 1, 3, 4, 2] 43 | 44 | it.skip('从collection中计算出每个数的个数', () => { 45 | const result = groupingCount(collection) 46 | 47 | expect(result).toEqual({ '1': 6, '2': 3, '3': 4, '4': 2 }) 48 | }) 49 | }) 50 | 51 | describe('chooseCommonElements', () => { 52 | const collection_a = ['a', 'e', 'h', 't', 'f', 'c', 'g', 'b', 'd'] 53 | const collection_b = ['a', 'd', 'e', 'f'] 54 | 55 | it.skip('选出A集合中与B集合中的共有元素', () => { 56 | const result = chooseCommonElements(collection_a, collection_b) 57 | 58 | expect(result).toEqual(['a', 'e', 'f', 'd']) 59 | }) 60 | }) 61 | 62 | describe('chooseNoCommonElements', () => { 63 | const collection_a = ['a', 'e', 'h', 't', 'f', 'c', 'g', 'b', 'd'] 64 | const collection_b = ['a', 'd', 'e', 'f'] 65 | 66 | it.skip('选出A集合中与B集合中的不共有元素', () => { 67 | const result = chooseNoCommonElements(collection_a, collection_b) 68 | 69 | expect(result).toEqual(['h', 't', 'c', 'g', 'b']) 70 | }) 71 | }) 72 | 73 | describe('chooseDivisibleInteger', () => { 74 | const collection_a = [4, 7, 9, 25, 16, 21, 64, 32, 35, 49] 75 | const collection_b = [2, 3, 5] 76 | 77 | it.skip('选出A集合中可以被B集合中整除的数', () => { 78 | const result = chooseDivisibleInteger(collection_a, collection_b) 79 | 80 | expect(result).toEqual([4, 9, 25, 16, 21, 64, 32, 35]) 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /collections/flatten/flatten.js: -------------------------------------------------------------------------------- 1 | export const twoDimensionalArrayToOne = (collection) => { 2 | return [collection] 3 | } 4 | 5 | export const twoDimensionalArrayToOneSorted = (collection) => { 6 | return [collection] 7 | } 8 | -------------------------------------------------------------------------------- /collections/flatten/flatten.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | twoDimensionalArrayToOneSorted, 3 | twoDimensionalArrayToOne, 4 | } from './flatten' 5 | 6 | describe('double_to_one', () => { 7 | const collection = [1, [2], [3, 4]] 8 | 9 | it.skip('把二维数组变成一维数组', () => { 10 | const result = twoDimensionalArrayToOne(collection) 11 | 12 | expect(result).toEqual([1, 2, 3, 4]) 13 | }) 14 | }) 15 | 16 | describe('twoDimensionalArrayToOneSorted', () => { 17 | const collection = [[1, 2, 3], [5, 2, 1, 4], [2, 1]] 18 | 19 | it.skip('把二维数组变成一维数组,消除重复,按照第一次出现的顺序排列最后的输出结果', () => { 20 | const result = twoDimensionalArrayToOneSorted(collection) 21 | 22 | expect(result).toEqual([1, 2, 3, 5, 4]) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /collections/map/map.js: -------------------------------------------------------------------------------- 1 | export const mapToEven = (collection) => { 2 | return [collection] 3 | } 4 | 5 | export const mapToFourMultiplesAddOne = (collection) => { 6 | return [collection] 7 | } 8 | 9 | export const mapToThreeMultiples = (collection) => { 10 | return [collection] 11 | } 12 | 13 | export const mapNumberToWord = (collection) => { 14 | return [collection] 15 | } 16 | 17 | export const mapNumberToWordOver26 = (collection) => { 18 | return [collection] 19 | } 20 | 21 | export const sortAscendingly = (collection) => { 22 | return [collection] 23 | } 24 | 25 | export const sortDescendingly = (collection) => { 26 | return [collection] 27 | } 28 | -------------------------------------------------------------------------------- /collections/map/map.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | mapToFourMultiplesAddOne, 3 | mapToEven, 4 | mapToThreeMultiples, 5 | mapNumberToWord, 6 | mapNumberToWordOver26, 7 | sortAscendingly, 8 | sortDescendingly, 9 | } from './map' 10 | 11 | describe('mapToEven', () => { 12 | const collection_a = [1, 2, 3, 4, 5] 13 | const collection_b = [2, 4, 6, 8, 10] 14 | 15 | it.skip('将集合A中得元素映射成集合B中的元素', () => { 16 | const result = mapToEven(collection_a) 17 | expect(result).toEqual(collection_b) 18 | }) 19 | }) 20 | 21 | describe('mapToFourMultiplesAddOne', () => { 22 | const collection_a = [1, 2, 3, 4, 5] 23 | const collection_b = [5, 9, 13, 17, 21] 24 | 25 | it.skip('四倍加一', () => { 26 | const result = mapToFourMultiplesAddOne(collection_a) 27 | expect(result).toEqual(collection_b) 28 | }) 29 | }) 30 | 31 | describe('mapToThreeMultiples', () => { 32 | const collection_a = [1, 3, 5, 4, 9] 33 | const collection_b = [3, 9, 15, 12, 27] 34 | 35 | it.skip('三倍映射', () => { 36 | const result = mapToThreeMultiples(collection_a) 37 | expect(result).toEqual(collection_b) 38 | }) 39 | }) 40 | 41 | describe('mapNumberToWord', () => { 42 | const collection_a = [1, 2, 3, 4, 5] 43 | const collection_b = ['a', 'b', 'c', 'd', 'e'] 44 | 45 | it.skip('数字映射为字母', () => { 46 | const result = mapNumberToWord(collection_a) 47 | expect(result).toEqual(collection_b) 48 | }) 49 | }) 50 | 51 | describe('mapNumberToWordOver26', () => { 52 | const collection_a = [1, 13, 27, 30, 25, 27] 53 | const collection_b = ['a', 'm', 'aa', 'ad', 'y', 'aa'] 54 | 55 | it.skip('字母表映射2', () => { 56 | const result = mapNumberToWordOver26(collection_a) 57 | expect(result).toEqual(collection_b) 58 | }) 59 | }) 60 | 61 | describe('sortAscendingly', () => { 62 | const collection_a = [3, 2, 4, 5, 6] 63 | const collection_b = [2, 3, 4, 5, 6] 64 | 65 | it.skip('从小到大排序', () => { 66 | const result = sortAscendingly(collection_a) 67 | expect(result).toEqual(collection_b) 68 | }) 69 | }) 70 | 71 | describe('sortDescendingly', () => { 72 | const collection_a = [3, 2, 4, 5, 6] 73 | const collection_b = [6, 5, 4, 3, 2] 74 | 75 | it.skip('从大到小排序', () => { 76 | const result = sortDescendingly(collection_a) 77 | expect(result).toEqual(collection_b) 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /collections/reduce/reduce.js: -------------------------------------------------------------------------------- 1 | export const collectMaxNumber = (collection) => { 2 | return [collection] 3 | } 4 | 5 | export const collectMinNumber = (collection) => { 6 | return [collection] 7 | } 8 | 9 | export const compareCollections = (collection_a, collection_b) => { 10 | return [collection_a, collection_b] 11 | } 12 | 13 | export const computeAverage = (collection) => { 14 | return [collection] 15 | } 16 | 17 | export const computeChainMedian = (collection) => { 18 | return [collection] 19 | } 20 | 21 | export const computeMedian = (collection) => { 22 | return [collection] 23 | } 24 | 25 | export const computeSum = (collection) => { 26 | return [collection] 27 | } 28 | 29 | export const calculateFirstSubscription = (collection, element) => { 30 | return [collection, element] 31 | } 32 | 33 | export const calculateLastSubscription = (collection, element) => { 34 | return [collection, element] 35 | } 36 | 37 | export const findFirstEven = (collection) => { 38 | return [collection] 39 | } 40 | 41 | export const findLastEven = (collection) => { 42 | return [collection] 43 | } 44 | 45 | export const splitToZero = (number, internal) => { 46 | return [number, internal] 47 | } 48 | -------------------------------------------------------------------------------- /collections/reduce/reduce.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | collectMinNumber, 3 | collectMaxNumber, 4 | compareCollections, 5 | computeAverage, 6 | computeChainMedian, 7 | computeMedian, 8 | computeSum, 9 | calculateFirstSubscription, 10 | calculateLastSubscription, 11 | findFirstEven, 12 | findLastEven, 13 | splitToZero, 14 | } from './reduce' 15 | 16 | describe('collectMaxNumber', () => { 17 | const collection = [1, 2, 3, 4, 5] 18 | 19 | it.skip('选出给定数字集合元素的最大值', () => { 20 | const result = collectMaxNumber(collection) 21 | expect(result).toEqual(5) 22 | }) 23 | }) 24 | 25 | describe('collectMinNumber', () => { 26 | const collection = [1, 8, 9, 21, 5] 27 | 28 | it.skip('选出给定数字集合元素的最小值', () => { 29 | const result = collectMinNumber(collection) 30 | expect(result).toEqual(1) 31 | }) 32 | }) 33 | 34 | describe('compareCollections', () => { 35 | const collection_a = [1, 11, 27, 20, 4, 9, 15] 36 | const collection_b = [1, 11, 27, 20, 4, 9, 15] 37 | 38 | it.skip('判断两个集合是否相同', () => { 39 | const result = compareCollections(collection_a, collection_b) 40 | 41 | expect(result).toEqual(true) 42 | }) 43 | }) 44 | 45 | describe('computeAverage', () => { 46 | const collection = [1, 3, 5, 98, 67, 453] 47 | 48 | it.skip('计算给定数字集合元素的平均值', () => { 49 | const result = computeAverage(collection) 50 | expect(result).toEqual(104.5) 51 | }) 52 | }) 53 | 54 | describe('computeChainMedian', () => { 55 | const chain = '1->4->6->2->3->10->9->8->11->20->19->30' 56 | 57 | it.skip('计算给定链表的中位数', () => { 58 | const result = computeChainMedian(chain) 59 | expect(result).toEqual(8.5) 60 | }) 61 | }) 62 | 63 | describe('computeMedian', () => { 64 | const collection_a = [1, 1, 1, 2, 3] 65 | const collection_b = [1, 1, 2, 3] 66 | const collection_c = [1, 4, 6, 2, 3, 10, 9, 8, 11, 20, 19, 30] 67 | 68 | it.skip('计算给定奇数个数字的集合的中位数', () => { 69 | const result = computeMedian(collection_a) 70 | expect(result).toEqual(1) 71 | }) 72 | 73 | it.skip('计算给定偶数个数字的集合的中位数', () => { 74 | const result = computeMedian(collection_b) 75 | expect(result).toEqual(1.5) 76 | }) 77 | 78 | it.skip('计算给定偶数个数字的集合的中位数-2', () => { 79 | const result = computeMedian(collection_c) 80 | expect(result).toEqual(8.5) 81 | }) 82 | }) 83 | 84 | describe('computeSum', () => { 85 | const collection = [1, 2, 3, 4, 5] 86 | 87 | it.skip('计算给定集合元素的总和', () => { 88 | const result = computeSum(collection) 89 | 90 | expect(result).toEqual(15) 91 | }) 92 | }) 93 | 94 | describe('calculateFirstSubscription', () => { 95 | const collection = [1, 11, 27, 20, 4, 9, 15, 4, 1, 11] 96 | const element = 4 97 | 98 | it.skip('找出某元素在给定集合中的第一个下标', () => { 99 | const result = calculateFirstSubscription(collection, element) 100 | 101 | expect(result).toEqual(4) 102 | }) 103 | }) 104 | 105 | describe('calculateLastSubscription', () => { 106 | const collection = [1, 11, 27, 20, 4, 9, 15, 4, 1, 11] 107 | const element = 4 108 | 109 | it.skip('找出某元素在给定集合中的最后一个下标', () => { 110 | const result = calculateLastSubscription(collection, element) 111 | 112 | expect(result).toEqual(7) 113 | }) 114 | }) 115 | 116 | describe('findFirstEven', () => { 117 | const collection = [1, 11, 27, 20, 4, 9, 15] 118 | 119 | it.skip('找出给定集合元素的第一个偶数', () => { 120 | const result = findFirstEven(collection) 121 | 122 | expect(result).toEqual(20) 123 | }) 124 | }) 125 | 126 | describe('findLastEven', () => { 127 | const collection = [1, 11, 27, 20, 4, 9, 15] 128 | 129 | it.skip('找出给定集合元素的最后一个偶数', () => { 130 | const result = findLastEven(collection) 131 | 132 | expect(result).toEqual(4) 133 | }) 134 | }) 135 | 136 | describe('splitToZero', () => { 137 | const collection_a = [0.8, 0.6, 0.4, 0.2, 0] 138 | const collection_b = [0.7, 0.4, 0.1, -0.2] 139 | 140 | it.skip('根据给定数字无限分割至等于0', () => { 141 | const result = splitToZero(0.8, 0.2) 142 | expect(result).toEqual(collection_a) 143 | }) 144 | 145 | it.skip('根据给定数字无限分割至小于0', () => { 146 | const result = splitToZero(0.7, 0.3) 147 | expect(result).toEqual(collection_b) 148 | }) 149 | }) 150 | -------------------------------------------------------------------------------- /collections/section/section.js: -------------------------------------------------------------------------------- 1 | export const collectSameElements = (collection_a, collection_b) => { 2 | return [collection_a, collection_b] 3 | } 4 | 5 | export const collectSameElementsInSubArray = (collection_a, collection_b) => { 6 | return [collection_a, collection_b] 7 | } 8 | 9 | export const collectSameElementsWithinObjectValue = ( 10 | collection_a, 11 | collection_b 12 | ) => { 13 | return [collection_a, collection_b] 14 | } 15 | 16 | export const collectSameElementsInBothObjectKeysAndValues = ( 17 | collection_a, 18 | collection_b 19 | ) => { 20 | return [collection_a, collection_b] 21 | } 22 | 23 | export const countSameElements = (collection) => { 24 | return [collection] 25 | } 26 | 27 | export const countSameElementsWithMultipleCounts = (collection) => { 28 | return [collection] 29 | } 30 | 31 | export const countSameElementsWithDifferentCountsFormat = (collection) => { 32 | return [collection] 33 | } 34 | 35 | export const createUpdatedCollection = (collection_a, object_b) => { 36 | return [collection_a, object_b] 37 | } 38 | 39 | export const createUpdatedCollection2 = (collection_a, object_b) => { 40 | return [collection_a, object_b] 41 | } 42 | 43 | export const countAndMinusIntersectionally = (collection_a, object_b) => { 44 | return [collection_a, object_b] 45 | } 46 | 47 | export const countAndMinusIntersectionallyWithMultiplesCounts = ( 48 | collection_a, 49 | object_b 50 | ) => { 51 | return [collection_a, object_b] 52 | } 53 | -------------------------------------------------------------------------------- /collections/section/section.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | collectSameElementsInSubArray, 3 | collectSameElements, 4 | collectSameElementsWithinObjectValue, 5 | collectSameElementsInBothObjectKeysAndValues, 6 | countSameElements, 7 | countSameElementsWithMultipleCounts, 8 | countSameElementsWithDifferentCountsFormat, 9 | createUpdatedCollection, 10 | createUpdatedCollection2, 11 | countAndMinusIntersectionally, 12 | countAndMinusIntersectionallyWithMultiplesCounts, 13 | } from './section' 14 | 15 | describe('collectSameElements', () => { 16 | const collection_a = ['a', 'e', 'h', 't', 'f', 'c', 'g', 'b', 'd'] 17 | const collection_b = ['a', 'd', 'e', 'f'] 18 | 19 | it.skip('选出A集合中与B集合中相同的元素', () => { 20 | const result = collectSameElements(collection_a, collection_b) 21 | 22 | expect(result).toEqual(['a', 'e', 'f', 'd']) 23 | }) 24 | }) 25 | 26 | describe('collectSameElementsInSubArray', () => { 27 | const collection_a = ['a', 'e', 'h', 't', 'f', 'c', 'g', 'b', 'd'] 28 | const collection_b = [['a', 'd', 'e', 'f']] 29 | 30 | it.skip('选出A集合中与B集合中子数组的元素相同的元素', () => { 31 | const result = collectSameElementsInSubArray(collection_a, collection_b) 32 | 33 | expect(result).toEqual(['a', 'e', 'f', 'd']) 34 | }) 35 | }) 36 | 37 | describe('collectSameElementsWithinObjectValue', () => { 38 | const collection_a = ['a', 'e', 'h', 't', 'f', 'c', 'g', 'b', 'd'] 39 | const collection_b = { value: ['a', 'd', 'e', 'f'] } 40 | 41 | it.skip('选出A集合中跟B对象中value属性中的元素相同的元素', () => { 42 | const result = collectSameElementsWithinObjectValue( 43 | collection_a, 44 | collection_b 45 | ) 46 | 47 | expect(result).toEqual(['a', 'e', 'f', 'd']) 48 | }) 49 | }) 50 | 51 | describe('collectSameElementsInBothObjectKeysAndValues', () => { 52 | const collection_a = [ 53 | { key: 'a' }, 54 | { key: 'e' }, 55 | { key: 'h' }, 56 | { key: 't' }, 57 | { key: 'f' }, 58 | { key: 'c' }, 59 | { key: 'g' }, 60 | { key: 'b' }, 61 | { key: 'd' }, 62 | ] 63 | const collection_b = { value: ['a', 'd', 'e', 'f'] } 64 | 65 | it.skip('选出A集合中元素的key属性,跟B对象中value属性中的元素相同的元素', () => { 66 | const result = collectSameElementsInBothObjectKeysAndValues( 67 | collection_a, 68 | collection_b 69 | ) 70 | 71 | expect(result).toEqual(['a', 'e', 'f', 'd']) 72 | }) 73 | }) 74 | 75 | describe('countSameElements', () => { 76 | const collection = [ 77 | 'a', 78 | 'a', 79 | 'a', 80 | 'e', 81 | 'e', 82 | 'e', 83 | 'e', 84 | 'e', 85 | 'e', 86 | 'e', 87 | 'h', 88 | 'h', 89 | 'h', 90 | 'h', 91 | 'h', 92 | 'h', 93 | 'h', 94 | 'h', 95 | 'h', 96 | 'h', 97 | 'h', 98 | 't', 99 | 't', 100 | 't', 101 | 't', 102 | 't', 103 | 't', 104 | 't', 105 | 't', 106 | 't', 107 | 't', 108 | 't', 109 | 't', 110 | 't', 111 | 't', 112 | 't', 113 | 't', 114 | 't', 115 | 't', 116 | 't', 117 | 't', 118 | 'f', 119 | 'f', 120 | 'f', 121 | 'f', 122 | 'f', 123 | 'f', 124 | 'f', 125 | 'f', 126 | 'f', 127 | 'c', 128 | 'c', 129 | 'c', 130 | 'c', 131 | 'c', 132 | 'c', 133 | 'c', 134 | 'c', 135 | 'g', 136 | 'g', 137 | 'g', 138 | 'g', 139 | 'g', 140 | 'g', 141 | 'g', 142 | 'b', 143 | 'b', 144 | 'b', 145 | 'b', 146 | 'b', 147 | 'b', 148 | 'd', 149 | 'd', 150 | 'd', 151 | 'd', 152 | 'd', 153 | ] 154 | 155 | it.skip('把A集合中相同的元素统计出数量', () => { 156 | const result = countSameElements(collection) 157 | 158 | expect(result).toEqual([ 159 | { key: 'a', count: 3 }, 160 | { key: 'e', count: 7 }, 161 | { key: 'h', count: 11 }, 162 | { key: 't', count: 20 }, 163 | { key: 'f', count: 9 }, 164 | { key: 'c', count: 8 }, 165 | { key: 'g', count: 7 }, 166 | { key: 'b', count: 6 }, 167 | { key: 'd', count: 5 }, 168 | ]) 169 | }) 170 | }) 171 | 172 | describe('countSameElementsWithMultipleCounts', () => { 173 | const collection = [ 174 | 'a', 175 | 'a', 176 | 'a', 177 | 'e', 178 | 'e', 179 | 'e', 180 | 'e', 181 | 'e', 182 | 'e', 183 | 'e', 184 | 'h', 185 | 'h', 186 | 'h', 187 | 'h', 188 | 'h', 189 | 'h', 190 | 'h', 191 | 'h', 192 | 'h', 193 | 'h', 194 | 'h', 195 | 't', 196 | 't', 197 | 't', 198 | 't', 199 | 't', 200 | 't', 201 | 't', 202 | 't', 203 | 't', 204 | 't', 205 | 't', 206 | 't', 207 | 't', 208 | 't', 209 | 't', 210 | 't', 211 | 't', 212 | 't', 213 | 't', 214 | 't', 215 | 'f', 216 | 'f', 217 | 'f', 218 | 'f', 219 | 'f', 220 | 'f', 221 | 'f', 222 | 'f', 223 | 'f', 224 | 'c', 225 | 'c', 226 | 'c', 227 | 'c', 228 | 'c', 229 | 'c', 230 | 'c', 231 | 'c', 232 | 'g', 233 | 'g', 234 | 'g', 235 | 'g', 236 | 'g', 237 | 'g', 238 | 'g', 239 | 'b', 240 | 'b', 241 | 'b', 242 | 'b', 243 | 'b', 244 | 'b', 245 | 'd-5', 246 | ] 247 | 248 | it.skip('把A集合中相同的元素统计出数量', () => { 249 | const result = countSameElementsWithMultipleCounts(collection) 250 | 251 | expect(result).toEqual([ 252 | { key: 'a', count: 3 }, 253 | { key: 'e', count: 7 }, 254 | { key: 'h', count: 11 }, 255 | { key: 't', count: 20 }, 256 | { key: 'f', count: 9 }, 257 | { key: 'c', count: 8 }, 258 | { key: 'g', count: 7 }, 259 | { key: 'b', count: 6 }, 260 | { key: 'd', count: 5 }, 261 | ]) 262 | }) 263 | }) 264 | 265 | describe('countSameElementsWithDifferentCountsFormat', () => { 266 | const collection = [ 267 | 'a', 268 | 'a', 269 | 'a', 270 | 'e', 271 | 'e', 272 | 'e', 273 | 'e', 274 | 'e', 275 | 'e', 276 | 'e', 277 | 'h', 278 | 'h', 279 | 'h', 280 | 'h', 281 | 'h', 282 | 'h', 283 | 'h[3]', 284 | 'h', 285 | 'h', 286 | 't', 287 | 't-2', 288 | 't', 289 | 't', 290 | 't', 291 | 't', 292 | 't', 293 | 't', 294 | 't[10]', 295 | 'f', 296 | 'f', 297 | 'f', 298 | 'f', 299 | 'f', 300 | 'f', 301 | 'f', 302 | 'f', 303 | 'f', 304 | 'c:8', 305 | 'g', 306 | 'g', 307 | 'g', 308 | 'g', 309 | 'g', 310 | 'g', 311 | 'g', 312 | 'b', 313 | 'b', 314 | 'b', 315 | 'b', 316 | 'b', 317 | 'b', 318 | 'd-5', 319 | ] 320 | 321 | it.skip('把A集合中相同的元素统计出数量', () => { 322 | const result = countSameElementsWithDifferentCountsFormat(collection) 323 | 324 | expect(result).toEqual([ 325 | { name: 'a', summary: 3 }, 326 | { name: 'e', summary: 7 }, 327 | { name: 'h', summary: 11 }, 328 | { name: 't', summary: 19 }, 329 | { name: 'f', summary: 9 }, 330 | { name: 'c', summary: 8 }, 331 | { name: 'g', summary: 7 }, 332 | { name: 'b', summary: 6 }, 333 | { name: 'd', summary: 5 }, 334 | ]) 335 | }) 336 | }) 337 | 338 | describe('createUpdatedCollection', () => { 339 | const collection_a = [ 340 | { key: 'a', count: 2 }, 341 | { key: 'e', count: 2 }, 342 | { key: 'h', count: 2 }, 343 | { key: 't', count: 2 }, 344 | { key: 'f', count: 2 }, 345 | { key: 'c', count: 2 }, 346 | { key: 'g', count: 2 }, 347 | { key: 'b', count: 2 }, 348 | { key: 'd', count: 2 }, 349 | ] 350 | 351 | const object_b = { value: ['a', 'd', 'e', 'f'] } 352 | 353 | it.skip('选出A集合中元素的key属性跟B对象中value属性中的元素相同的元素,把他们的count-1,输出减过之后的新A集合', () => { 354 | const result = createUpdatedCollection(collection_a, object_b) 355 | 356 | expect(result).toEqual([ 357 | { key: 'a', count: 1 }, 358 | { key: 'e', count: 1 }, 359 | { key: 'h', count: 2 }, 360 | { key: 't', count: 2 }, 361 | { key: 'f', count: 1 }, 362 | { key: 'c', count: 2 }, 363 | { key: 'g', count: 2 }, 364 | { key: 'b', count: 2 }, 365 | { key: 'd', count: 1 }, 366 | ]) 367 | }) 368 | }) 369 | 370 | describe('createUpdatedCollection2', () => { 371 | const collection_a = [ 372 | { key: 'a', count: 3 }, 373 | { key: 'e', count: 7 }, 374 | { key: 'h', count: 11 }, 375 | { key: 't', count: 20 }, 376 | { key: 'f', count: 9 }, 377 | { key: 'c', count: 8 }, 378 | { key: 'g', count: 7 }, 379 | { key: 'b', count: 6 }, 380 | { key: 'd', count: 5 }, 381 | ] 382 | 383 | const object_b = { value: ['a', 'd', 'e', 'f'] } 384 | 385 | it.skip('选出A集合中元素的key属性跟B对象中value属性中的元素相同的元素,把他们的count,满3减1,输出减过之后的新A集', () => { 386 | const result = createUpdatedCollection2(collection_a, object_b) 387 | 388 | expect(result).toEqual([ 389 | { key: 'a', count: 2 }, 390 | { key: 'e', count: 5 }, 391 | { key: 'h', count: 11 }, 392 | { key: 't', count: 20 }, 393 | { key: 'f', count: 6 }, 394 | { key: 'c', count: 8 }, 395 | { key: 'g', count: 7 }, 396 | { key: 'b', count: 6 }, 397 | { key: 'd', count: 4 }, 398 | ]) 399 | }) 400 | }) 401 | 402 | describe('countAndMinusIntersectionally', () => { 403 | const collection_a = [ 404 | 'a', 405 | 'a', 406 | 'a', 407 | 'e', 408 | 'e', 409 | 'e', 410 | 'e', 411 | 'e', 412 | 'e', 413 | 'e', 414 | 'h', 415 | 'h', 416 | 'h', 417 | 'h', 418 | 'h', 419 | 'h', 420 | 'h', 421 | 'h', 422 | 'h', 423 | 'h', 424 | 'h', 425 | 't', 426 | 't', 427 | 't', 428 | 't', 429 | 't', 430 | 't', 431 | 't', 432 | 't', 433 | 't', 434 | 't', 435 | 't', 436 | 't', 437 | 't', 438 | 't', 439 | 't', 440 | 't', 441 | 't', 442 | 't', 443 | 't', 444 | 't', 445 | 'f', 446 | 'f', 447 | 'f', 448 | 'f', 449 | 'f', 450 | 'f', 451 | 'f', 452 | 'f', 453 | 'f', 454 | 'c', 455 | 'c', 456 | 'c', 457 | 'c', 458 | 'c', 459 | 'c', 460 | 'c', 461 | 'c', 462 | 'g', 463 | 'g', 464 | 'g', 465 | 'g', 466 | 'g', 467 | 'g', 468 | 'g', 469 | 'b', 470 | 'b', 471 | 'b', 472 | 'b', 473 | 'b', 474 | 'b', 475 | 'd', 476 | 'd', 477 | 'd', 478 | 'd', 479 | 'd', 480 | ] 481 | 482 | const object_b = { value: ['a', 'd', 'e', 'f'] } 483 | 484 | it.skip('统计出A集合中相同的元素的个数,形成C集合,C集合中的元素要形如{key:"a", count: 3},然后选出C集合中的元素的key属性跟B对象中value属性中的元素相同的元素,把他们的count,满3减1,输出减过之后的新C集合', () => { 485 | const result = countAndMinusIntersectionally(collection_a, object_b) 486 | 487 | expect(result).toEqual([ 488 | { key: 'a', count: 2 }, 489 | { key: 'e', count: 5 }, 490 | { key: 'h', count: 11 }, 491 | { key: 't', count: 20 }, 492 | { key: 'f', count: 6 }, 493 | { key: 'c', count: 8 }, 494 | { key: 'g', count: 7 }, 495 | { key: 'b', count: 6 }, 496 | { key: 'd', count: 4 }, 497 | ]) 498 | }) 499 | }) 500 | 501 | describe('countAndMinusIntersectionallyWithMultiplesCounts', () => { 502 | const collection_a = [ 503 | 'a', 504 | 'a', 505 | 'a', 506 | 'e', 507 | 'e', 508 | 'e', 509 | 'e', 510 | 'e', 511 | 'e', 512 | 'e', 513 | 'h', 514 | 'h', 515 | 'h', 516 | 'h', 517 | 'h', 518 | 'h', 519 | 'h', 520 | 'h', 521 | 'h', 522 | 'h', 523 | 'h', 524 | 't', 525 | 't', 526 | 't', 527 | 't', 528 | 't', 529 | 't', 530 | 't', 531 | 't', 532 | 't', 533 | 't', 534 | 't', 535 | 't', 536 | 't', 537 | 't', 538 | 't', 539 | 't', 540 | 't', 541 | 't', 542 | 't', 543 | 't', 544 | 'f', 545 | 'f', 546 | 'f', 547 | 'f', 548 | 'f', 549 | 'f', 550 | 'f', 551 | 'f', 552 | 'f', 553 | 'c', 554 | 'c', 555 | 'c', 556 | 'c', 557 | 'c', 558 | 'c', 559 | 'c', 560 | 'c', 561 | 'g', 562 | 'g', 563 | 'g', 564 | 'g', 565 | 'g', 566 | 'g', 567 | 'g', 568 | 'b', 569 | 'b', 570 | 'b', 571 | 'b', 572 | 'b', 573 | 'b', 574 | 'd-5', 575 | ] 576 | 577 | const object_b = { value: ['a', 'd', 'e', 'f'] } 578 | 579 | it.skip('统计出A集合中相同的元素的个数,有过有-就把-右边的数字也计算入个数,形成C集合,C集合中的元素要形如{key:"a", count: 3},然后选出C集合中的元素的key属性跟B对象中value属性中的元素相同的元素,把他们的count,满3减1,输出减过之后的新C集合', () => { 580 | const result = countAndMinusIntersectionallyWithMultiplesCounts( 581 | collection_a, 582 | object_b 583 | ) 584 | 585 | expect(result).toEqual([ 586 | { key: 'a', count: 2 }, 587 | { key: 'e', count: 5 }, 588 | { key: 'h', count: 11 }, 589 | { key: 't', count: 20 }, 590 | { key: 'f', count: 6 }, 591 | { key: 'c', count: 8 }, 592 | { key: 'g', count: 7 }, 593 | { key: 'b', count: 6 }, 594 | { key: 'd', count: 4 }, 595 | ]) 596 | }) 597 | }) 598 | -------------------------------------------------------------------------------- /collections/super-position-operation/collection-operation/collection-operation.js: -------------------------------------------------------------------------------- 1 | export const evenToLetter = (collection) => { 2 | return collection 3 | } 4 | 5 | export const averageToLetter = (collection) => { 6 | return collection 7 | } 8 | 9 | export const multipleAndComputeSum = (collection) => { 10 | return collection 11 | } 12 | 13 | export const multipleOddAndAdd = (collection) => { 14 | return collection 15 | } 16 | 17 | export const multipleOddAndComputeSum = (collection) => { 18 | return collection 19 | } 20 | -------------------------------------------------------------------------------- /collections/super-position-operation/collection-operation/collection-operation.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | averageToLetter, 3 | evenToLetter, 4 | multipleOddAndAdd, 5 | multipleAndComputeSum, 6 | multipleOddAndComputeSum, 7 | } from './collection-operation' 8 | 9 | describe('even_to_letter', () => { 10 | const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 11 | 12 | it.skip('数组的每个偶数映射为字母', () => { 13 | const result = evenToLetter(collection) 14 | 15 | expect(result).toEqual(['b', 'd', 'f', 'h', 'j']) 16 | }) 17 | }) 18 | 19 | describe('average_to_letter', () => { 20 | const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 21 | 22 | it.skip('数组的平均数(如果是小数上取整)映射为字母', () => { 23 | const result = averageToLetter(collection) 24 | 25 | expect(result).toEqual('f') 26 | }) 27 | }) 28 | 29 | describe('multipleAndComputeSum', () => { 30 | const collection = [1, 5, 7, 11, 35, 67] 31 | 32 | it.skip('每一个数*3 +2再算总和', () => { 33 | const result = multipleAndComputeSum(collection) 34 | 35 | expect(result).toEqual(390) 36 | }) 37 | }) 38 | 39 | describe('multipleOddAndComputeSum', () => { 40 | const collection = [1, 5, 7, 12, 11, 35, 54, 67, 70] 41 | 42 | it.skip('每一个奇数*3+2', () => { 43 | const result = multipleOddAndAdd(collection) 44 | 45 | expect(result).toEqual([5, 17, 23, 35, 107, 203]) 46 | }) 47 | }) 48 | 49 | describe('multipleOddAndComputeSum', () => { 50 | const collection = [1, 5, 7, 12, 11, 35, 54, 67, 70] 51 | 52 | it.skip('每一个奇数*3+5再求总和', () => { 53 | const result = multipleOddAndComputeSum(collection) 54 | 55 | expect(result).toEqual(408) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /collections/super-position-operation/interval-operation/interval-operation.js: -------------------------------------------------------------------------------- 1 | export const computerEvenSum = (collection) => { 2 | return [collection] 3 | } 4 | 5 | export const computeOddSum = (collection) => { 6 | return [collection] 7 | } 8 | 9 | export const medianToLetter = (collection) => { 10 | return [collection] 11 | } 12 | -------------------------------------------------------------------------------- /collections/super-position-operation/interval-operation/interval-operation.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | computeOddSum, 3 | computerEvenSum, 4 | medianToLetter, 5 | } from './interval-operation' 6 | 7 | describe('amount_even', () => { 8 | const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 9 | 10 | it.skip('1-10的偶数算总数', () => { 11 | const result = computerEvenSum(collection) 12 | 13 | expect(result).toEqual(30) 14 | }) 15 | }) 16 | 17 | describe('computeOddSum', () => { 18 | const collection = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 19 | 20 | it.skip('1-10的奇数算平均数', () => { 21 | const result = computeOddSum(collection) 22 | 23 | expect(result).toEqual(5) 24 | }) 25 | }) 26 | 27 | describe('medianToLetter', () => { 28 | const collection = [ 29 | 20, 30 | 21, 31 | 22, 32 | 23, 33 | 24, 34 | 25, 35 | 26, 36 | 27, 37 | 28, 38 | 29, 39 | 30, 40 | 31, 41 | 32, 42 | 33, 43 | 34, 44 | 35, 45 | 36, 46 | 37, 47 | 38, 48 | 39, 49 | 40, 50 | 41, 51 | 42, 52 | 43, 53 | 44, 54 | 45, 55 | 46, 56 | 47, 57 | 48, 58 | 49, 59 | 50, 60 | 51, 61 | 52, 62 | 53, 63 | ] 64 | 65 | it.skip('(20,53)的中位数(如果是小数上取整)对应的字母', () => { 66 | const result = medianToLetter(collection) 67 | 68 | expect(result).toEqual('ak') 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /collections/super-position-operation/own-elements-operation/own-elements-operation.js: -------------------------------------------------------------------------------- 1 | export const calculateAverage = (collection) => { 2 | return collection 3 | } 4 | 5 | export const calculateMedian = (collection) => { 6 | return collection 7 | } 8 | 9 | export const evenGroupCalculateAverage = (collection) => { 10 | return collection 11 | } 12 | 13 | export const isExistElement = (collection, element) => { 14 | return [collection, element] 15 | } 16 | 17 | export const singleElement = (collection) => { 18 | return collection 19 | } 20 | 21 | export const evenAscOddDesc = (collection) => { 22 | return collection 23 | } 24 | 25 | export const oneAddNextMultiplyThree = (collection) => { 26 | return collection 27 | } 28 | 29 | export const rankByTwoLargeOneSmall = (collection) => { 30 | return collection 31 | } 32 | -------------------------------------------------------------------------------- /collections/super-position-operation/own-elements-operation/own-elements-operation.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | calculateMedian, 3 | calculateAverage, 4 | evenAscOddDesc, 5 | oneAddNextMultiplyThree, 6 | rankByTwoLargeOneSmall, 7 | evenGroupCalculateAverage, 8 | isExistElement, 9 | singleElement, 10 | } from './own-elements-operation' 11 | 12 | describe('calculateAverage', () => { 13 | const collection_a = [1, 2, 3, 4, 5, 6] 14 | 15 | it.skip('计算第偶数个元素的平均数', () => { 16 | const result = calculateAverage(collection_a) 17 | expect(result).toEqual(4) 18 | }) 19 | }) 20 | 21 | describe('calculateMedian', () => { 22 | const collection_a = [1, 2, 3, 4, 5, 6] 23 | const collection_b = [1, 2, 3, 4, 5, 6, 7, 8, 9] 24 | 25 | it.skip('集合中第偶数个元素的个数为奇数时,计算所有第偶数个元素的中位数', () => { 26 | const result = calculateMedian(collection_a) 27 | expect(result).toEqual(4) 28 | }) 29 | 30 | it.skip('集合中第偶数个元素的个数为偶数时,计算所有第偶数个元素的中位数', () => { 31 | const result = calculateMedian(collection_b) 32 | expect(result).toEqual(5) 33 | }) 34 | }) 35 | 36 | describe('evenGroupCalculateAverage', () => { 37 | const collection_a = [ 38 | 1, 39 | 2, 40 | 3, 41 | 4, 42 | 5, 43 | 6, 44 | 12, 45 | 454, 46 | 324, 47 | 21, 48 | 45, 49 | 644, 50 | 34, 51 | 56, 52 | 345, 53 | 570, 54 | 8, 55 | 4, 56 | 14, 57 | ] 58 | const collection_b = [1, 3, 5, 7, 33, 55, 31, 555, 777] 59 | const collection_c = [344, 256, 55, 777, 322, 180] 60 | 61 | it.skip('首先选出所有第偶数个元素,然后选出其中的偶数,按几位数分组,并计算每组的平均数', () => { 62 | const result = evenGroupCalculateAverage(collection_a) 63 | expect(result).toEqual([4, 56, 556]) 64 | }) 65 | 66 | it.skip('首先选出所有第偶数个元素,当不含有偶数时', () => { 67 | const result = evenGroupCalculateAverage(collection_b) 68 | expect(result).toEqual([0]) 69 | }) 70 | 71 | it.skip('首先选出所有第偶数个元素,然后选出其中的偶数,按几位数分组,当不含有1位,2位的数字,但含有3位的情况时,计算这组的平均数', () => { 72 | const result = evenGroupCalculateAverage(collection_c) 73 | expect(result).toEqual([218]) 74 | }) 75 | }) 76 | 77 | describe('isExistElement', () => { 78 | const collection_a = [1, 2, 3, 4, 5, 6] 79 | 80 | it.skip('下标为偶数的元素中,存在3', () => { 81 | const result = isExistElement(collection_a, 3) 82 | expect(result).toEqual(true) 83 | }) 84 | 85 | it.skip('下标为偶数的元素中,不存在4', () => { 86 | const result = isExistElement(collection_a, 4) 87 | expect(result).toEqual(false) 88 | }) 89 | }) 90 | 91 | describe('singleElement', () => { 92 | const collection_a = [1, 2, 3, 2, 5, 6, 21, 43, 12, 5] 93 | const collection_b = [11, 11, 22, 11, 33, 11] 94 | 95 | it.skip('第偶数个元素中,选出不重复的元素', () => { 96 | const result = singleElement(collection_a) 97 | expect(result).toEqual([6, 43, 5]) 98 | }) 99 | 100 | it.skip('第偶数个元素中,选出不重复的元素', () => { 101 | const result = singleElement(collection_b) 102 | expect(result).toEqual([]) 103 | }) 104 | }) 105 | 106 | describe('evenAscOddDesc', () => { 107 | const collection_a = [4, 32, 12, 45, 67, 46, 2, 53, 68, 54, 11] 108 | const collection_b = [2, 4, 12, 32, 46, 54, 68, 67, 53, 45, 11] 109 | 110 | it.skip('偶数奇数分两头,偶数部分从小到大,奇数部分从大到小', () => { 111 | const result = evenAscOddDesc(collection_a) 112 | expect(result).toEqual(collection_b) 113 | }) 114 | }) 115 | 116 | describe('oneAddNextMultiplyThree', () => { 117 | const collection_a = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21] 118 | const collection_b = [12, 24, 36, 48, 60, 72, 84, 96, 108, 120] 119 | 120 | it.skip('每一个数与下一个数相加,乘以3,最后一个数不算', () => { 121 | const result = oneAddNextMultiplyThree(collection_a) 122 | expect(result).toEqual(collection_b) 123 | }) 124 | }) 125 | 126 | describe('rankByTwoLargeOneSmall', () => { 127 | const collection_a = [2, 8, 1, 9, 6, 4, 3, 10] 128 | 129 | it.skip('两大一小排序', () => { 130 | const result = rankByTwoLargeOneSmall(collection_a) 131 | expect(result).toEqual([2, 3, 1, 6, 8, 4, 9, 10]) 132 | }) 133 | }) 134 | -------------------------------------------------------------------------------- /es6/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLin-TWer/frontend-practices/43ce10a77aa49a97a951fa9c8c9757cb77e9b933/es6/.gitkeep -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLin-TWer/frontend-practices/43ce10a77aa49a97a951fa9c8c9757cb77e9b933/jest.setup.js -------------------------------------------------------------------------------- /lodash/comparison/diffs.js: -------------------------------------------------------------------------------- 1 | export const compareDiffs = (one, another) => { 2 | return [one, another] 3 | } 4 | -------------------------------------------------------------------------------- /lodash/comparison/diffs.test.js: -------------------------------------------------------------------------------- 1 | import { compareDiffs } from './diffs' 2 | 3 | it.skip('should compare diffs of two data set', () => { 4 | const one = { 5 | details: [ 6 | { 7 | product: 'id-1', 8 | subProducts: [ 9 | { subProduct: 'id-11', price: 20 }, 10 | { subProduct: 'id-12', price: 30 }, 11 | { subProduct: 'id-13', price: 40 }, 12 | ], 13 | }, 14 | { 15 | product: 'id-2', 16 | subProducts: [ 17 | { subProduct: 'id-21', price: 200 }, 18 | { subProduct: 'id-22', price: 300 }, 19 | { subProduct: 'id-23', price: 400 }, 20 | ], 21 | }, 22 | ], 23 | } 24 | const another = { 25 | result: [ 26 | { groupKey: 'product/id-1/sub/id-11', price: 25 }, 27 | { groupKey: 'product/id-1/sub/id-12', price: 34 }, 28 | { groupKey: 'product/id-1/sub/id-13', price: 38 }, 29 | { groupKey: 'product/id-2/sub/id-21', price: 212 }, 30 | { groupKey: 'product/id-2/sub/id-22', price: 288 }, 31 | { groupKey: 'product/id-3/sub/id-23', price: 450 }, 32 | ], 33 | } 34 | 35 | const result = compareDiffs(one, another) 36 | 37 | expect(result).toEqual({ 38 | 'product/id-1/sub/id-11': { diffs: -5 }, 39 | 'product/id-1/sub/id-12': { diffs: -4 }, 40 | 'product/id-1/sub/id-13': { diffs: 2 }, 41 | 'product/id-2/sub/id-21': { diffs: -12 }, 42 | 'product/id-2/sub/id-22': { diffs: 12 }, 43 | 'product/id-2/sub/id-23': { diffs: -50 }, 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /lodash/count-elements/index.js: -------------------------------------------------------------------------------- 1 | export function countElements(collection) { 2 | return [collection] 3 | } 4 | 5 | export function countCategories(collection) { 6 | return [collection] 7 | } 8 | -------------------------------------------------------------------------------- /lodash/count-elements/index.test.js: -------------------------------------------------------------------------------- 1 | import { countCategories, countElements } from './index' 2 | 3 | it.skip('should return object with element counts', () => { 4 | const collection = ['a', 'a', 'b', 'c'] 5 | 6 | const result = countElements(collection) 7 | 8 | expect(result).toEqual({ 9 | a: 2, 10 | b: 1, 11 | c: 1, 12 | }) 13 | }) 14 | 15 | it.skip('should count same elements based on a field on the array', () => { 16 | const collection = [ 17 | { category: 'shoes', name: 'oxford' }, 18 | { category: 'shoes', name: 'converse' }, 19 | { category: 'clothing', name: 'karma' }, 20 | ] 21 | 22 | const result = countCategories(collection) 23 | 24 | expect(result).toEqual({ 25 | shoes: 2, 26 | clothing: 1, 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /lodash/group/apply-calculation-to-group.js: -------------------------------------------------------------------------------- 1 | export const applyCalculationToGroup = (data, groupMapping) => { 2 | return [data, groupMapping] 3 | } 4 | -------------------------------------------------------------------------------- /lodash/group/apply-calculation-to-group.test.js: -------------------------------------------------------------------------------- 1 | import { applyCalculationToGroup } from './apply-calculation-to-group' 2 | 3 | it.skip('should apply calculation to different groups according to key', () => { 4 | const groupMapping = { 5 | 652: 'education', 6 | 653: 'transportation', 7 | 654: 'meals', 8 | } 9 | const data = [ 10 | { expense: 5, code: 652 }, 11 | { expense: 10, code: 653 }, 12 | { expense: 15, code: 654 }, 13 | { expense: 20, code: 653 }, 14 | { expense: 25, code: 654 }, 15 | { expense: 30, code: 999 }, 16 | ] 17 | 18 | const result = applyCalculationToGroup(data, groupMapping) 19 | 20 | expect(result).toEqual({ 21 | education: 5, 22 | transportation: 30, 23 | meals: 40, 24 | others: 30, 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "eslint collections refactor tdd", 4 | "test": "jest", 5 | "test:watch": "jest --watch", 6 | "test:ci": "jest --coverage" 7 | }, 8 | "dependencies": { 9 | "lodash": "^4.17.13", 10 | "ramda": "^0.25.0" 11 | }, 12 | "devDependencies": { 13 | "@babel/core": "^7.4.4", 14 | "@babel/plugin-proposal-class-properties": "^7.4.4", 15 | "@babel/plugin-proposal-optional-chaining": "^7.2.0", 16 | "@babel/plugin-proposal-private-methods": "^7.4.4", 17 | "@babel/preset-env": "^7.4.4", 18 | "babel-eslint": "^9.0.0", 19 | "eslint": "^5.16.0", 20 | "eslint-config-prettier": "^3.6.0", 21 | "eslint-plugin-prettier": "^2.7.0", 22 | "husky": "^1.3.1", 23 | "jest": "^24.7.1", 24 | "lint-staged": "^7.2.2", 25 | "prettier": "^1.17.0" 26 | }, 27 | "jest": { 28 | "testMatch": [ 29 | "/**/*.test.js" 30 | ], 31 | "setupFilesAfterEnv": [ 32 | "/jest.setup.js" 33 | ], 34 | "verbose": false, 35 | "collectCoverage": false 36 | }, 37 | "prettier": { 38 | "printWidth": 80, 39 | "tabWidth": 2, 40 | "semi": false, 41 | "singleQuote": true, 42 | "trailingComma": "es5", 43 | "arrowParens": "always" 44 | }, 45 | "babel": { 46 | "presets": [ 47 | "@babel/preset-env" 48 | ], 49 | "plugins": [ 50 | "@babel/plugin-proposal-optional-chaining", 51 | "@babel/plugin-proposal-private-methods", 52 | "@babel/plugin-proposal-class-properties" 53 | ] 54 | }, 55 | "husky": { 56 | "hooks": { 57 | "pre-commit": "lint-staged" 58 | } 59 | }, 60 | "lint-staged": { 61 | "linters": { 62 | "*.js": [ 63 | "npm run lint -- --fix", 64 | "git add" 65 | ] 66 | }, 67 | "ignore": [ 68 | "node_modules/" 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /refactor/word-frequency/README.md: -------------------------------------------------------------------------------- 1 | # Word Frequency 重构 2 | 3 | 这是一个开发人员、需求人员都已经离职的代码,我们也不知道需求是什么,代码还写的很烂。 4 | 5 | 谢天谢地,这里面还有测试,让我们可以从测试看懂他们的业务,但是代码实在太烂了,咱们来重构一下。 6 | -------------------------------------------------------------------------------- /refactor/word-frequency/index.js: -------------------------------------------------------------------------------- 1 | export const getResult = (inputStr) => { 2 | if (inputStr.split(' ').length === 1) { 3 | return `${inputStr} 1` 4 | } else { 5 | const arr = inputStr.split(' ').filter((input) => input) 6 | let inputList = [] 7 | 8 | for (const s of arr) { 9 | const input = new Input(s, 1) 10 | inputList.push(input) 11 | } 12 | 13 | // get the map for the next step of sizing the same word 14 | const map = getListMap(inputList) 15 | const list = [] 16 | 17 | for (const entry of Object.keys(map)) { 18 | const input = new Input(entry, map[entry].length) 19 | list.push(input) 20 | } 21 | inputList = list 22 | inputList.sort((w1, w2) => w2.getWordCount() - w1.getWordCount()) 23 | 24 | const result = [] 25 | for (const w of inputList) { 26 | result.push(`${w.getValue()} ${w.getWordCount()}`) 27 | } 28 | 29 | return result.join('\n') 30 | } 31 | } 32 | 33 | const getListMap = (inputList) => { 34 | const map = {} 35 | for (const input of inputList) { 36 | if (!map.hasOwnProperty(input.getValue())) { 37 | const arr = [] 38 | arr.push(input) 39 | map[input.getValue()] = arr 40 | } else { 41 | map[input.getValue()].push(input) 42 | } 43 | } 44 | 45 | return map 46 | } 47 | 48 | class Input { 49 | constructor(w, i) { 50 | this.value = w 51 | this.count = i 52 | } 53 | 54 | getValue() { 55 | return this.value 56 | } 57 | 58 | getWordCount() { 59 | return this.count 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /refactor/word-frequency/index.test.js: -------------------------------------------------------------------------------- 1 | import { getResult } from './index' 2 | 3 | it('should get "the 1" when input "the"', () => { 4 | const input = 'the' 5 | const output = 'the 1' 6 | 7 | validate_Input_words_process_to_expected_word(input, output) 8 | }) 9 | 10 | it('should process two words', () => { 11 | const input = 'the is' 12 | const output = 'the 1\nis 1' 13 | 14 | validate_Input_words_process_to_expected_word(input, output) 15 | }) 16 | 17 | it('should process two words with special spaces', () => { 18 | const input = 'the is' 19 | const output = 'the 1\nis 1' 20 | 21 | validate_Input_words_process_to_expected_word(input, output) 22 | }) 23 | 24 | it('should pracess two same words with sorted', () => { 25 | const input = 'the the is' 26 | const output = 'the 2\nis 1' 27 | 28 | validate_Input_words_process_to_expected_word(input, output) 29 | }) 30 | 31 | it('should process sorted with count descending', () => { 32 | const input = 'the is is' 33 | const output = 'is 2\nthe 1' 34 | 35 | validate_Input_words_process_to_expected_word(input, output) 36 | }) 37 | 38 | it('should process two same words with sorted', () => { 39 | const input = 'the the is' 40 | const output = 'the 2\nis 1' 41 | 42 | validate_Input_words_process_to_expected_word(input, output) 43 | }) 44 | 45 | const validate_Input_words_process_to_expected_word = (input, output) => { 46 | // When 47 | const result = getResult(input) 48 | // Then 49 | expect(result).toBe(output) 50 | } 51 | -------------------------------------------------------------------------------- /scripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLin-TWer/frontend-practices/43ce10a77aa49a97a951fa9c8c9757cb77e9b933/scripts/.gitkeep -------------------------------------------------------------------------------- /tdd/anagrams/README.md: -------------------------------------------------------------------------------- 1 | ## 异位构词 2 | 3 | -------------------------------------------------------------------------------- /tdd/anagrams/index.js: -------------------------------------------------------------------------------- 1 | function findAllPossibleTwoWordsCombination() { 2 | return [] 3 | } 4 | 5 | export function isAnagramToWordDocument(words) { 6 | return words.every((word) => 7 | Array.from(word).every((character) => 'document'.includes(character)) 8 | ) 9 | } 10 | 11 | export const anagrams = () => { 12 | const combinations = findAllPossibleTwoWordsCombination() 13 | return combinations.filter(isAnagramToWordDocument) 14 | } 15 | -------------------------------------------------------------------------------- /tdd/anagrams/index.test.js: -------------------------------------------------------------------------------- 1 | import { isAnagramToWordDocument } from './index' 2 | 3 | describe('anagrams', () => { 4 | it('should return true when do and men are both anagrams of document', () => { 5 | expect(isAnagramToWordDocument(['do', 'men'])).toEqual(true) 6 | }) 7 | 8 | it('should return false when dir is not an anagram of document', () => { 9 | expect(isAnagramToWordDocument(['do', 'dir'])).toEqual(false) 10 | }) 11 | 12 | it('should return false when the second word is not an anagram of document', () => { 13 | expect(isAnagramToWordDocument(['do', 'dior'])).toEqual(false) 14 | }) 15 | 16 | it('should return false when the first word is not an anagram of document', () => { 17 | expect(isAnagramToWordDocument(['dior', 'do'])).toEqual(false) 18 | }) 19 | 20 | it('should return false when the second word is not an anagram of document', () => { 21 | expect(isAnagramToWordDocument(['do', 'bot'])).toEqual(false) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /tdd/args/README.md: -------------------------------------------------------------------------------- 1 | # Args 2 | 3 | This Kata is presented in Robert C.. Martin’s book “Clean Code”, chapter 14. 4 | 5 | ## Problem Description 6 | 7 | Most of us have had to parse command-line arguments from time to time. If we don’t have a convenient utility, then we simply walk the array of strings that is passed into the main function. There are several good utilities available from various sources, but they probably don’t do exactly what we want. So let’s write another one! 8 | 9 | The arguments passed to the program consist of flags and values. Flags should be one character, preceded by a minus sign. Each flag should have zero, or one value associated with it. 10 | 11 | You should write a parser for this kind of arguments. This parser takes a schema detailing what arguments the program expects. The schema specifies the number and types of flags and values the program expects. 12 | 13 | Once the schema has been specified, the program should pass the actual argument list to the args parser. It will verify that the arguments match the schema. The program can then ask the args parser for each of the values, using the names of the flags. The values are returned with the correct types, as specified in the schema. 14 | 15 | For example if the program is to be called with these arguments: 16 | 17 | ``` 18 | -l -p 8080 -d /usr/logs 19 | ``` 20 | 21 | this indicates a schema with 3 flags: l, p, d. The “l” (logging) flag has no values associated with it, it is a boolean flag, True if present, False if not. the “p” (port) flag has an integer value, and the “d” (directory) flag has a string value. 22 | 23 | If a flag mentioned in the schema is missing in the arguments, a suitable default value should be returned. For example “False” for a boolean, 0 for a number, and “” for a string. If the arguments given do not match the schema, it is important that a good error message is given, explaining exactly what is wrong. 24 | 25 | If you are feeling ambitious, extend your code to support lists eg 26 | 27 | ``` 28 | -g this,is,a,list -d 1,2,-3,5 29 | ``` 30 | 31 | So the “g” flag indicates a list of strings, `[“this”, “is”, “a”, “list”]` and the “d” flag indicates a list of integers, `[1, 2, -3, 5]`. 32 | 33 | Make sure your code is extensible, in that it is straightforward and obvious how to add new types of values. 34 | 35 | ## Clues 36 | 37 | What the schema should look like and how to specify it is deliberately left vague in the Kata description. An important part of the Kata is to design a concise yet readable format for it. 38 | 39 | ## Suggested Test Cases 40 | 41 | - make sure you have a test with a negative integer (confusing - sign) 42 | the order of the arguments need not match the order given in the schema. 43 | - have some tests that suitable default values are correctly assigned if flags given in the schema are missing in the args given. 44 | - Comments from those who are working on this Kata 45 | 46 | In Robert C. Martin’s book there is a full worked solution written in Java. He mentions in a footnote on page 200 that he has also solved it in Ruby. His Java code is available on [github.com/unclebob/javaargs/tree/master](https://www.github.com/unclebob/javaargs/tree/master), and the Ruby version is available on [github.com/unclebob/rubyargs/tree/master](https://www.github.com/unclebob/rubyargs/tree/master) 47 | -------------------------------------------------------------------------------- /tdd/args/args-parser/index.js: -------------------------------------------------------------------------------- 1 | import { Args } from '../args' 2 | 3 | export class ArgsParser { 4 | #schemas 5 | 6 | constructor(schemas) { 7 | this.#schemas = schemas 8 | } 9 | 10 | parse() { 11 | return new Args() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tdd/args/args-parser/index.test.js: -------------------------------------------------------------------------------- 1 | import { Schema } from '../schema' 2 | import { ArgsParser } from './index' 3 | 4 | it('acceptance tests', () => { 5 | const schemas = [new Schema('p', 8080)] 6 | const argumentList = '-p 8080' 7 | 8 | const parser = new ArgsParser(schemas) 9 | const args = parser.parse(argumentList) 10 | 11 | expect(args.get('p')).toEqual(8080) 12 | }) 13 | -------------------------------------------------------------------------------- /tdd/args/args/index.js: -------------------------------------------------------------------------------- 1 | export class Args { 2 | get() { 3 | return 8080 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tdd/args/schema/index.js: -------------------------------------------------------------------------------- 1 | export class Schema { 2 | #name 3 | #value 4 | 5 | constructor(name, value) { 6 | this.#name = name 7 | this.#value = value 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tdd/bowling/README.md: -------------------------------------------------------------------------------- 1 | # Bowling 2 | 3 | This description is based on that at [Adventures in C#: The Bowling Game](https://ronjeffries.com/xprog/articles/acsbowling/). 4 | 5 | ## Problem Description 6 | 7 | Create a program, which, given a valid sequence of rolls for one line of American Ten-Pin Bowling, produces the total score for the game. Here are some things that the program will not do: 8 | 9 | - We will not check for valid rolls. 10 | - We will not check for correct number of rolls and frames. 11 | - We will not provide scores for intermediate frames. 12 | 13 | Depending on the application, this might or might not be a valid way to define a complete story, but we do it here for purposes of keeping the kata light. I think you’ll see that improvements like those above would go in readily if they were needed for real. 14 | 15 | We can briefly summarize the scoring for this form of bowling: 16 | 17 | - Each game, or “line” of bowling, includes ten turns, or “frames” for the bowler. 18 | - In each frame, the bowler gets up to two tries to knock down all the pins. 19 | - If in two tries, he fails to knock them all down, his score for that frame is the total number of pins knocked down in his two tries. 20 | - If in two tries he knocks them all down, this is called a “spare” and his score for the frame is ten plus the number of pins knocked down on his next throw (in his next turn). 21 | - If on his first try in the frame he knocks down all the pins, this is called a “strike”. His turn is over, and his score for the frame is ten plus the simple total of the pins knocked down in his next two rolls. 22 | - If he gets a spare or strike in the last (tenth) frame, the bowler gets to throw one or two more bonus balls, respectively. These bonus throws are taken as part of the same turn. If the bonus throws knock down all the pins, the process does not repeat: the bonus throws are only used to calculate the score of the final frame. 23 | - The game score is the total of all frame scores. 24 | 25 | More info on the rules at: [How to Score for Bowling](http://www.topendsports.com/sport/tenpin/scoring.htm) 26 | 27 | ## Clues 28 | 29 | What makes this game interesting to score is the lookahead in the scoring for strike and spare. At the time we throw a strike or spare, we cannot calculate the frame score: we have to wait one or two frames to find out what the bonus is. 30 | 31 | ## Suggested Test Cases 32 | 33 | (When scoring “X” indicates a strike, “/” indicates a spare, “-” indicates a miss) 34 | 35 | - `X X X X X X X X X X X X` (12 rolls: 12 strikes) = 10 frames * 30 points = 300 36 | - `9- 9- 9- 9- 9- 9- 9- 9- 9- 9-` (20 rolls: 10 pairs of 9 and miss) = 10 frames * 9 points = 90 37 | - 5/ 5/ 5/ 5/ 5/ 5/ 5/ 5/ 5/ 5/5 (21 rolls: 10 pairs of 5 and spare, with a final 5) = 10 frames * 15 points = 150 38 | 39 | ## Comments from those who have mastered this Kata 40 | 41 | Write some thoughts here about what you have learnt from this Kata. You don’t have to post all the code of your solution - I think the solution in itself is less interesting than the path you took to get there and what decisions you made. Just seeing the code won’t necessarily help me to reproduce it for myself. So in this section various people might go through the main parts of the problem and how they tackled them, what design ideas were discarded, and which order the test cases were implemented in. 42 | 43 | * One interesting point to note is that without counting frames in any way (although I don’t think this was intended as a ‘hard’ requirement for the initial Kata completion), finding an elegant way to identify the end of the game/last “real” frame becomes difficult (ie: assuming there are final ‘bonus’ rolls included in a given test case). Update : After trying various things, including writing out a logic matrix for possible end-of-game combinations, I’m not sure it’s possible to detect whether a final ‘throw’ counts as bonus-only or as part of an actual frame, unless you’re counting frames. – [RudyXDesjardins](http://codingdojo.org/people/RudyXDesjardins/) 44 | 45 | [KataBowlingByAndreasLarsson](http://codingdojo.org/solution/KataBowlingByAndreasLarsson/) 46 | -------------------------------------------------------------------------------- /tdd/bowling/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthanLin-TWer/frontend-practices/43ce10a77aa49a97a951fa9c8c9757cb77e9b933/tdd/bowling/index.js -------------------------------------------------------------------------------- /tdd/bowling/index.test.js: -------------------------------------------------------------------------------- 1 | it('test suite must contain at least one test', () => { 2 | expect(1).toEqual(1) 3 | }) 4 | -------------------------------------------------------------------------------- /tdd/fizzbuzz/README.md: -------------------------------------------------------------------------------- 1 | # FizBuzzWhizz 2 | 3 | ## About this Kata 4 | 5 | This Kata was posted here by someone anonymously. Michael Feathers and EmilyBache performed it at agile2008 when competing in “Programming with the stars” in python, in 4 minutes. 6 | 7 | Difficulty: Easy Good for teaching: TDD , BabySteps 8 | 9 | ## Problem Description 10 | 11 | Imagine the scene. You are eleven years old, and in the five minutes before the end of the lesson, your Maths teacher decides he should make his class more “fun” by introducing a “game”. He explains that he is going to point at each pupil in turn and ask them to say the next number in sequence, starting from one. The “fun” part is that if the number is divisible by three, you instead say “Fizz” and if it is divisible by five you say “Buzz”. So now your maths teacher is pointing at all of your classmates in turn, and they happily shout “one!”, “two!”, “Fizz!”, “four!”, “Buzz!”… until he very deliberately points at you, fixing you with a steely gaze… time stands still, your mouth dries up, your palms become sweatier and sweatier until you finally manage to croak “Fizz!”. Doom is avoided, and the pointing finger moves on. 12 | 13 | So of course in order to avoid embarassment infront of your whole class, you have to get the full list printed out so you know what to say. Your class has about 33 pupils and he might go round three times before the bell rings for breaktime. Next maths lesson is on Thursday. Get coding! 14 | 15 | Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz “. 16 | 17 | Sample output: 18 | 19 | ``` 20 | 1 21 | 2 22 | Fizz 23 | 4 24 | Buzz 25 | Fizz 26 | 7 27 | 8 28 | Fizz 29 | Buzz 30 | 11 31 | Fizz 32 | 13 33 | 14 34 | FizzBuzz 35 | 16 36 | 17 37 | Fizz 38 | 19 39 | Buzz 40 | ... etc up to 100 41 | ``` 42 | 43 | ## Stage 2 - new requirements 44 | 45 | - A number is fizz if it is divisible by 3 or if it has a 3 in it 46 | - A number is buzz if it is divisible by 5 or if it has a 5 in it 47 | 48 | ## Results 49 | 50 | 01:05:57 Sunday, June 30, 2019: 5min 51 | 01:18:16 Sunday, June 30, 2019: 3min 47s 52 | 10:55:08 Sunday, June 30, 2019: 3min 25s 53 | 11:17:19 Sunday, June 30, 2019: 3min 38s 54 | 11:22:54 Sunday, June 30, 2019: 3min 32s 55 | 11:33:37 Sunday, June 30, 2019: 2min 12s -> parameterized testing 56 | 15:19:43 Sunday, June 30, 2019: 2min 4s 57 | 15:28:34 Sunday, June 30, 2019: 1min 54s 58 | 15:32:52 Sunday, June 30, 2019: 1min 43s 59 | 15:35:24 Sunday, June 30, 2019: 1min 37s 60 | 15:45:30 Sunday, June 30, 2019: 1min 33s 61 | 16:12:50 Sunday, June 30, 2019: 1min 28s 62 | -------------------------------------------------------------------------------- /tdd/fizzbuzz/index.js: -------------------------------------------------------------------------------- 1 | function shouldSayFizz(number) { 2 | return number % 3 === 0 || /3/.test(number) 3 | } 4 | 5 | function shouldSayBuzz(number) { 6 | return number % 5 === 0 || /5/.test(number) 7 | } 8 | 9 | export function fizzbuzz(number) { 10 | if (shouldSayFizz(number) && shouldSayBuzz(number)) { 11 | return 'FizzBuzz' 12 | } 13 | if (shouldSayFizz(number)) { 14 | return 'Fizz' 15 | } 16 | if (shouldSayBuzz(number)) { 17 | return 'Buzz' 18 | } 19 | return number 20 | } 21 | -------------------------------------------------------------------------------- /tdd/fizzbuzz/index.test.js: -------------------------------------------------------------------------------- 1 | import { fizzbuzz } from './index' 2 | 3 | test.each([ 4 | [1, 1], 5 | [2, 2], 6 | [3, 'Fizz'], 7 | [5, 'Buzz'], 8 | [15, 'FizzBuzz'], 9 | [13, 'Fizz'], 10 | [59, 'Buzz'], 11 | [53, 'FizzBuzz'], 12 | ])('fizzbuzz(%s) should return %s', (input, expected) => { 13 | expect(fizzbuzz(input)).toEqual(expected) 14 | }) 15 | -------------------------------------------------------------------------------- /tdd/guess-number/README.md: -------------------------------------------------------------------------------- 1 | ## 练习描述 2 | 3 | 实现猜数字的游戏。游戏有四个格子,每个格子有一个 0 到 9 的数字,任意两个格子的数字都不一样。你有 6 次猜测的机会,如果猜对则获胜,否则失败。每次猜测时需依序输入 4 个数字,程序会根据猜测的情况给出 xAxB 的反馈,A 前面的数字代表位置和数字都对的个数,B 前面的数字代表数字对但是位置不对的个数。 4 | 5 | 例如:答案是 1 2 3 4, 那么对于不同的输入,有如下的输出 6 | 7 | **Example**: 8 | 9 | > 答案是 1 2 3 4, 那么对于不同的输入,有如下的输出 10 | 11 | ``` 12 | Input      Output             Instruction 13 | 1 5 6 7      1A0B               1 correct 14 | 2 4 7 8      0A2B               2 and 4 wrong position 15 | 0 3 2 4      1A2B               4 correct,2 and 3 wrong position 16 | 5 6 7 8      0A0B               all wrong 17 | 4 3 2 1      0A4B               4 numbers position wrong 18 | 1 2 3 4      4A0B               win, all correct 19 | 1 1 2 3     Wrong Input,Input again 20 | 1 2        Wrong Input,Input again 21 | ``` 22 | 23 | 答案在游戏开始时随机生成。输入只有 6 次机会,在每次猜测时,程序应给出当前猜测的结果,以及之前所有猜测的数字和结果以供玩家参考。输入界面为控制台(Console),以避免太多与问题无关的界面代码。 24 | 25 | 输入时,用空格分隔数字。 26 | 27 | 要求: 28 | 29 | - 设计和编写测试用例 30 | - 完成已有测试文件中的测试,并保证此测试文件所测类的测试覆盖率为 100% 31 | - 单元测试应涵盖所有核心业务逻辑 32 | - 用小步骤进行单元测试重构 33 | - 为单元测试和方法命名有意义的名称 34 | - 代码通过小步骤提交并附上意义的评论 35 | -------------------------------------------------------------------------------- /tdd/lcd/README.md: -------------------------------------------------------------------------------- 1 | # 编程小道场 2 | 3 | 把数字转化为 lcd 灯的表现形式,打印在控制台上。下面是 0~9 的样子: 4 | 5 | ``` 6 | ._. ... ._. ._. ... ._. ._. ._. ._. ._. 7 | |.| ..| ._| ._| |_| |_. |_. ..| |_| |_| 8 | |_| ..| |_. ._| ..| ._| |_| ..| |_| ..| 9 | ``` 10 | 11 | 例子: 910 12 | 13 | ``` 14 | ._. ... ._. 15 | |_| ..| |.| 16 | ..| ..| |_| 17 | ``` 18 | 19 | ## 参考资料 20 | 21 | - [Array#join](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join?v=example) 22 | - [Array#push](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push?v=example) 23 | - [Sting Reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) 24 | -------------------------------------------------------------------------------- /tdd/lcd/index.js: -------------------------------------------------------------------------------- 1 | export const main = (numberString) => { 2 | let numberArray = numberString.split('') 3 | let line1 = [] 4 | let line2 = [] 5 | let line3 = [] 6 | for (let number of numberArray) { 7 | line1.push(numberMap[number][0]) 8 | line2.push(numberMap[number][1]) 9 | line3.push(numberMap[number][2]) 10 | } 11 | 12 | return ( 13 | line1.join(' ') + '\n' + line2.join(' ') + '\n' + line3.join(' ') + '\n' 14 | ) 15 | } 16 | 17 | let numberMap = { 18 | '0': ['._.', '|.|', '|_|'], 19 | '1': ['...', '..|', '..|'], 20 | '2': ['._.', '._|', '|_.'], 21 | '3': ['._.', '._|', '._|'], 22 | '4': ['...', '|_|', '..|'], 23 | '5': ['._.', '|_.', '._|'], 24 | '6': ['._.', '|_.', '|_|'], 25 | '7': ['._.', '..|', '..|'], 26 | '8': ['._.', '|_|', '|_|'], 27 | '9': ['._.', '|_|', '..|'], 28 | } 29 | -------------------------------------------------------------------------------- /tdd/lcd/index.test.js: -------------------------------------------------------------------------------- 1 | import { main } from './index' 2 | 3 | describe('main()', () => { 4 | it('should return 910', () => { 5 | // prettier-ignore 6 | expect(main('910')).toBe( 7 | '._. ... ._.' + '\n' + 8 | '|_| ..| |.|' + '\n' + 9 | '..| ..| |_|' + '\n' 10 | ) 11 | }) 12 | 13 | it('should return 256', () => { 14 | // prettier-ignore 15 | expect(main('256')).toBe( 16 | '._. ._. ._.' + '\n' + 17 | '._| |_. |_.' + '\n' + 18 | '|_. ._| |_|' + '\n' 19 | ) 20 | }) 21 | 22 | it('should return 7', () => { 23 | // prettier-ignore 24 | expect(main('7')).toBe( 25 | '._.' + '\n' + 26 | '..|' + '\n' + 27 | '..|' + '\n' 28 | ) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /tdd/pos-v1/README.md: -------------------------------------------------------------------------------- 1 | # POS Project v1 2 | 3 | POS 收银机 v1 4 | 5 | ## 需求描述 6 | 7 | 商店里进行购物结算时会使用收银机(POS)系统,这台收银机会在结算时根据客户的购物车(Cart)中的商品(Item)和商店正在进行的优惠活动(Promotion)进行结算和打印购物清单。 8 | 9 | 已知该商店正在对部分商品进行“买二赠一”的优惠活动。 10 | 11 | 我们需要实现一个名为 `printInventory` 函数,该函数能够将指定格式的数据作为参数输入,然后在浏览器的控制台中输出结算清单的文本。 12 | 13 | 输入格式(样例): 14 | 15 | ```javascript 16 | [ 17 | 'ITEM000001', 18 | 'ITEM000001', 19 | 'ITEM000001', 20 | 'ITEM000001', 21 | 'ITEM000001', 22 | 'ITEM000003-2', 23 | 'ITEM000005', 24 | 'ITEM000005', 25 | 'ITEM000005', 26 | ] 27 | ``` 28 | 29 | 其中对'ITEM000003-2'来说,"-"之前的是标准的条形码,"-"之后的是数量。 30 | 当我们购买需要称量的物品的时候,由称量的机器生成此类条形码,收银机负责识别生成小票。 31 | 32 | 清单内容(样例): 33 | 34 | ``` 35 | ***<没钱赚商店>购物清单*** 36 | 名称:可口可乐,数量:3瓶,单价:3.00(元),小计:6.00(元) 37 | 名称:羽毛球,数量:5个,单价:1.00(元),小计:4.00(元) 38 | 名称:苹果,数量:2斤,单价:5.50(元),小计:11.00(元) 39 | ---------------------- 40 | 挥泪赠送商品: 41 | 名称:可口可乐,数量:1瓶 42 | 名称:羽毛球,数量:1个 43 | ---------------------- 44 | 总计:21.00(元) 45 | 节省:4.00(元) 46 | ********************** 47 | ``` 48 | 49 | ## 作业提示 50 | 51 | 可使用`loadItems()`方法获取全部的商品,该方法返回结果为一个包含了商品对象的数组(样例): 52 | 53 | ```javascript 54 | [ item1, item2, item3, ..., itemN ] 55 | ``` 56 | 57 | 每一个商品对象的结构如下(样例): 58 | 59 | ```javascript 60 | { 61 | barcode: 'ITEM000000', 62 | name: '可口可乐', 63 | unit: '瓶', 64 | price: 3.00 65 | } 66 | ``` 67 | 68 | 可使用`loadPromotions()`方法获取全部的促销信息,该方法返回结果为一个包含有促销信息对象的数组(样例): 69 | 70 | ```javascript 71 | [ 72 | { 73 | type: 'BUY_TWO_GET_ONE_FREE', 74 | barcodes: ['ITEM000000', 'ITEM000001'], 75 | }, 76 | ] 77 | ``` 78 | 79 | ## 如何使用 80 | 81 | ```bash 82 | npm install 83 | npm test 84 | ``` 85 | -------------------------------------------------------------------------------- /tdd/pos-v1/data/items.js: -------------------------------------------------------------------------------- 1 | export const loadItems = () => [ 2 | { 3 | barcode: 'ITEM000000', 4 | name: '可口可乐', 5 | unit: '瓶', 6 | price: 3.0, 7 | }, 8 | { 9 | barcode: 'ITEM000001', 10 | name: '雪碧', 11 | unit: '瓶', 12 | price: 3.0, 13 | }, 14 | { 15 | barcode: 'ITEM000002', 16 | name: '苹果', 17 | unit: '斤', 18 | price: 5.5, 19 | }, 20 | { 21 | barcode: 'ITEM000003', 22 | name: '荔枝', 23 | unit: '斤', 24 | price: 15.0, 25 | }, 26 | { 27 | barcode: 'ITEM000004', 28 | name: '电池', 29 | unit: '个', 30 | price: 2.0, 31 | }, 32 | { 33 | barcode: 'ITEM000005', 34 | name: '方便面', 35 | unit: '袋', 36 | price: 4.5, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /tdd/pos-v1/data/promotions.js: -------------------------------------------------------------------------------- 1 | export const loadPromotions = () => [ 2 | { 3 | type: 'BUY_TWO_GET_ONE_FREE', 4 | barcodes: ['ITEM000000', 'ITEM000001', 'ITEM000005'], 5 | }, 6 | ] 7 | -------------------------------------------------------------------------------- /tdd/pos-v1/index.js: -------------------------------------------------------------------------------- 1 | export const printInventory = (input) => { 2 | return input 3 | } 4 | -------------------------------------------------------------------------------- /tdd/pos-v1/index.test.js: -------------------------------------------------------------------------------- 1 | import { printInventory } from './index' 2 | 3 | describe('pos', () => { 4 | const inputs = [ 5 | 'ITEM000001', 6 | 'ITEM000001', 7 | 'ITEM000001', 8 | 'ITEM000001', 9 | 'ITEM000001', 10 | 'ITEM000003-2', 11 | 'ITEM000005', 12 | 'ITEM000005', 13 | 'ITEM000005', 14 | ] 15 | 16 | it.skip('should print correct text', () => { 17 | const result = printInventory(inputs) 18 | 19 | const expected = 20 | '***<没钱赚商店>购物清单***\n' + 21 | '名称:雪碧,数量:5瓶,单价:3.00(元),小计:12.00(元)\n' + 22 | '名称:荔枝,数量:2斤,单价:15.00(元),小计:30.00(元)\n' + 23 | '名称:方便面,数量:3袋,单价:4.50(元),小计:9.00(元)\n' + 24 | '----------------------\n' + 25 | '挥泪赠送商品:\n' + 26 | '名称:雪碧,数量:1瓶\n' + 27 | '名称:方便面,数量:1袋\n' + 28 | '----------------------\n' + 29 | '总计:51.00(元)\n' + 30 | '节省:7.50(元)\n' + 31 | '**********************' 32 | 33 | expect(result).toEqual(expected) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /tdd/take-out-food/README.md: -------------------------------------------------------------------------------- 1 | # 黄焖鸡 2 | 3 | ## 需求描述 4 | 5 | 某快餐品牌推出了它独家的外卖应用,用户可以在手机上直接下单。该应用会根据用户选择的菜品(Item)、数量(Count)和优惠方式(Promotion)进行计算,告诉用户需要支付的金额(Charge)。 6 | 7 | 优惠活动有多种形式。假设用户一次只能使用一种优惠,那么使用哪种优惠省钱最多就会是一个让用户头疼的问题。所以该外卖应用为了方便用户,在用户下单时,会自动选择最优惠的方式并计算出最终金额让用户确认。 8 | 9 | 我们需要实现一个名为`bestCharge`的函数,它能够接收用户选择的菜品和数量(以特定格式呈现)作为输入,然后返回计算后的汇总信息。 10 | 11 | 已知: 12 | 13 | - 该店的菜品每一个都有一个唯一的id 14 | - 当前的优惠方式有: 15 | - 满30减6元 16 | - 指定菜品半价 17 | - 除菜品外没有其它收费(如送餐费、餐盒费等) 18 | - 如果两种优惠方式省钱一样多,则使用前一种优惠方式 19 | 20 | 输入样例 21 | ------- 22 | 23 | ``` 24 | ["ITEM0001 x 1", "ITEM0013 x 2", "ITEM0022 x 1"] 25 | ``` 26 | 27 | 输出样例 28 | ------- 29 | 30 | ``` 31 | ============= 订餐明细 ============= 32 | 黄焖鸡 x 1 = 18元 33 | 肉夹馍 x 2 = 12元 34 | 凉皮 x 1 = 8元 35 | ----------------------------------- 36 | 使用优惠: 37 | 指定菜品半价(黄焖鸡,凉皮),省13元 38 | ----------------------------------- 39 | 总计:25元 40 | =================================== 41 | ``` 42 | 43 | 使用另一种优惠的样例 44 | ------------------ 45 | 46 | 输入: 47 | 48 | ``` 49 | ["ITEM0013 x 4", "ITEM0022 x 1"] 50 | ``` 51 | 52 | 53 | 输出: 54 | 55 | ``` 56 | ============= 订餐明细 ============= 57 | 肉夹馍 x 4 = 24元 58 | 凉皮 x 1 = 8元 59 | ----------------------------------- 60 | 使用优惠: 61 | 满30减6元,省6元 62 | ----------------------------------- 63 | 总计:26元 64 | =================================== 65 | ``` 66 | 67 | 如果没有优惠可享受 68 | --------------- 69 | 70 | 输入: 71 | 72 | ``` 73 | ["ITEM0013 x 4"] 74 | ``` 75 | 76 | 输出: 77 | 78 | ``` 79 | ============= 订餐明细 ============= 80 | 肉夹馍 x 4 = 24元 81 | ----------------------------------- 82 | 总计:24元 83 | =================================== 84 | ``` 85 | 86 | 87 | ## 基础作业 88 | 89 | 1. 相关代码在`src`目录下 90 | 1. 实现`best-charge.js`中的`bestCharge`函数 91 | 1. 写代码前先使用tasking整理思路并画出管道图 92 | 1. 先写测试再写实现,代码须跟管道图匹配 93 | 1. 代码整洁、函数粒度合适、命名有意义 94 | 95 | 96 | ## 作业提示 97 | 98 | 1. 可使用`loadAllItems()`方法获取全部的菜品 99 | 2. 可使用`loadPromotions()`方法获取全部的优惠方式 100 | 101 | ## 运行测试 102 | 103 | ### 浏览器 104 | 105 | 可使用浏览器打开`run-specs.html`文件运行测试 106 | 107 | ### 命令行 108 | 109 | 首先使用`node -v`命令确定你的`node`版本为`6.x`。 110 | 111 | 如果你安装了`nvm`,可通过如下方式切换: 112 | 113 | ``` 114 | nvm use 6 115 | node -v 116 | ``` 117 | 118 | 然后进入本项目根目录: 119 | 120 | ``` 121 | npm install 122 | npm test 123 | ``` 124 | 125 | 就可以看到在命令行中运行测试并输出报告。 126 | -------------------------------------------------------------------------------- /tdd/take-out-food/data/items.js: -------------------------------------------------------------------------------- 1 | function loadAllItems() { 2 | return [ 3 | { 4 | id: 'ITEM0001', 5 | name: '黄焖鸡', 6 | price: 18.0, 7 | }, 8 | { 9 | id: 'ITEM0013', 10 | name: '肉夹馍', 11 | price: 6.0, 12 | }, 13 | { 14 | id: 'ITEM0022', 15 | name: '凉皮', 16 | price: 8.0, 17 | }, 18 | { 19 | id: 'ITEM0030', 20 | name: '冰锋', 21 | price: 2.0, 22 | }, 23 | ] 24 | } 25 | 26 | module.exports = loadAllItems 27 | -------------------------------------------------------------------------------- /tdd/take-out-food/data/promotions.js: -------------------------------------------------------------------------------- 1 | function loadPromotions() { 2 | return [ 3 | { 4 | type: '满30减6元', 5 | }, 6 | { 7 | type: '指定菜品半价', 8 | items: ['ITEM0001', 'ITEM0022'], 9 | }, 10 | ] 11 | } 12 | 13 | module.exports = loadPromotions 14 | -------------------------------------------------------------------------------- /tdd/take-out-food/index.js: -------------------------------------------------------------------------------- 1 | export const bestCharge = () => { 2 | return '' 3 | } 4 | -------------------------------------------------------------------------------- /tdd/take-out-food/index.test.js: -------------------------------------------------------------------------------- 1 | import { bestCharge } from './index' 2 | 3 | describe('Take out food', () => { 4 | it.skip('should generate best charge when best is 指定菜品半价', () => { 5 | let inputs = ['ITEM0001 x 1', 'ITEM0013 x 2', 'ITEM0022 x 1'] 6 | let summary = bestCharge(inputs).trim() 7 | let expected = ` 8 | ============= 订餐明细 ============= 9 | 黄焖鸡 x 1 = 18元 10 | 肉夹馍 x 2 = 12元 11 | 凉皮 x 1 = 8元 12 | ----------------------------------- 13 | 使用优惠: 14 | 指定菜品半价(黄焖鸡,凉皮),省13元 15 | ----------------------------------- 16 | 总计:25元 17 | ===================================`.trim() 18 | expect(summary).toEqual(expected) 19 | }) 20 | 21 | it.skip('should generate best charge when best is 满30减6元', () => { 22 | let inputs = ['ITEM0013 x 4', 'ITEM0022 x 1'] 23 | let summary = bestCharge(inputs).trim() 24 | let expected = ` 25 | ============= 订餐明细 ============= 26 | 肉夹馍 x 4 = 24元 27 | 凉皮 x 1 = 8元 28 | ----------------------------------- 29 | 使用优惠: 30 | 满30减6元,省6元 31 | ----------------------------------- 32 | 总计:26元 33 | ===================================`.trim() 34 | expect(summary).toEqual(expected) 35 | }) 36 | 37 | it.skip('should generate best charge when no promotion can be used', () => { 38 | let inputs = ['ITEM0013 x 4'] 39 | let summary = bestCharge(inputs).trim() 40 | let expected = ` 41 | ============= 订餐明细 ============= 42 | 肉夹馍 x 4 = 24元 43 | ----------------------------------- 44 | 总计:24元 45 | ===================================`.trim() 46 | expect(summary).toEqual(expected) 47 | }) 48 | }) 49 | --------------------------------------------------------------------------------