├── template ├── docs │ ├── .nojekyll │ ├── CNAME │ ├── logo.png │ ├── favicon.ico │ ├── contribution.md │ ├── roadmap.md │ ├── functions.md │ ├── function-template.md │ ├── rankings.md │ ├── _sidebar.md │ ├── quick-start.md │ ├── index.html │ ├── _coverpage.md │ ├── introduction.md │ └── submit-a-solution.md └── npm │ ├── package.json │ └── winners.js ├── logo.png ├── src ├── test.spec.ts ├── helper.ts ├── solution-cli.ts ├── functions │ ├── index.ts │ ├── is-object.ts │ ├── is-null.ts │ ├── is-undefined.ts │ ├── primes.ts │ ├── is-array.ts │ ├── is-prime.ts │ ├── is-string.ts │ ├── average.ts │ ├── fibonacci.ts │ ├── is-number.ts │ ├── times.ts │ ├── is-empty-array.ts │ ├── flatten.ts │ ├── is-palindrome.ts │ ├── factorial.ts │ ├── max.ts │ └── equal.ts ├── solution.ts ├── registry.ts ├── benchmark.ts └── build.ts ├── prettier.config.js ├── tslint.json ├── tsconfig.json ├── jest.config.js ├── LICENSE ├── package.json ├── .gitignore ├── README.md └── .circleci └── config.yml /template/docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/docs/CNAME: -------------------------------------------------------------------------------- 1 | rapidashjs.com -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acanguven/rapidash/HEAD/logo.png -------------------------------------------------------------------------------- /src/test.spec.ts: -------------------------------------------------------------------------------- 1 | import { Registry } from './registry'; 2 | 3 | Registry.runSolutionTests(); 4 | -------------------------------------------------------------------------------- /template/docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acanguven/rapidash/HEAD/template/docs/logo.png -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'es5', 4 | }; 5 | -------------------------------------------------------------------------------- /template/docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acanguven/rapidash/HEAD/template/docs/favicon.ico -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "gts/tslint.json", 3 | "linterOptions": { 4 | "exclude": [ 5 | "**/*.json" 6 | ] 7 | }, 8 | "rules": { 9 | "no-any": false 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/gts/tsconfig-google.json", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "allowSyntheticDefaultImports": true 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/helper.ts: -------------------------------------------------------------------------------- 1 | const distinctByProp = (arr: T[], byId: keyof T): T[] => { 2 | const map = new Map(); 3 | arr.forEach(val => { 4 | map.set(val[byId], val); 5 | }); 6 | return Array.from(map.values()); 7 | }; 8 | 9 | export { distinctByProp }; 10 | -------------------------------------------------------------------------------- /template/docs/contribution.md: -------------------------------------------------------------------------------- 1 | # Contribution 2 | 3 | You can easily join the contest and submit solutions, follow [submit a solution guide](submit-a-solution.md) to start. 4 | 5 | You can also create pull requests for core parts of this project, you can also add new problems. 6 | -------------------------------------------------------------------------------- /template/docs/roadmap.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | - [ ] ES Module 4 | - [ ] Export Typings 5 | - [ ] Coverage for Builder (Requires refactoring) 6 | - [ ] Running benchmarks parallel at CircleCi 7 | 8 | 9 | [Contributions](contribution.md) are welcomed. Feel free to support us by opening pull requests. 10 | -------------------------------------------------------------------------------- /template/docs/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | 8 | 9 |

10 | All examples assume that you required Rapidash 11 |

12 | 13 | ```js 14 | const r = require('rapidashjs'); 15 | ``` 16 | 17 | {{functionsContent}} 18 | -------------------------------------------------------------------------------- /template/docs/function-template.md: -------------------------------------------------------------------------------- 1 | ___ 2 | ## {{functionName}} 3 | 4 | {{functionDescription}} 5 | 6 | ### Examples 7 | {{functionExample}} 8 | 9 | 10 | > {{functionName}} function by {{solutionOwner}} 11 | 12 | {{functionName}} can perform approximately {{hz}} ops/sec. 13 | 14 | -------------------------------------------------------------------------------- /template/docs/rankings.md: -------------------------------------------------------------------------------- 1 | # Rankings 2 | 3 |

4 | Submitting a solution is really easy, join this list! 5 |

6 | 7 | ___ 8 | **These are the great people who make Rapidash great.** 9 | 10 | 11 | | Rank | Avatar | Username | Solutions | Score | 12 | |------|--------|-----------|-------|-----------| 13 | {{rankings}} 14 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | testPathIgnorePatterns: ["build"], 5 | collectCoverageFrom: [ 6 | "**/src/functions/*.ts" 7 | ], 8 | coverageThreshold: { 9 | global: { 10 | branches: 100, 11 | functions: 100, 12 | lines: 100, 13 | statements: 100 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /template/docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | 5 |

6 | 7 | * [Introduction](/introduction.md) 8 | * [Quick Start](/quick-start.md) 9 | * [Contribution](contribution.md) 10 | * [Submit A Solution](submit-a-solution.md) 11 | * [Rankings](rankings.md) 12 | * [Roadmap](roadmap.md) 13 | * [Functions](functions.md) 14 | {{sideBarItems}} 15 | -------------------------------------------------------------------------------- /template/npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rapidash", 3 | "version": "1.4.0", 4 | "main": "./rapidash.min.js", 5 | "description": "Fastest functional utility library created by NodeJs community", 6 | "scripts": { 7 | "postinstall": "node winners.js" 8 | }, 9 | "keywords": [ 10 | "javascript", 11 | "utility", 12 | "library" 13 | ], 14 | "homepage": "https://rapidashjs.com", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/acanguven/rapidash.git" 18 | }, 19 | "license": "MIT" 20 | } 21 | -------------------------------------------------------------------------------- /template/npm/winners.js: -------------------------------------------------------------------------------- 1 | console.log(' ', 'Rapidash 🎠'); //cyan 2 | console.log('✅', 'Thanks for using Rapidash'); 3 | console.log('✅', 'Rapidash is built by automated benchmarks from submitted solutions of community members'); 4 | console.log('✅', 'Join the contest and contribute to Rapidash'); 5 | console.log('✅', 'https://github.com/Acanguven/rapidash'); 6 | console.log('✅', 'Be one of the greatest contributors by creating new solutions'); 7 | console.log(''); 8 | console.log(''); 9 | console.log('📈', 'Top 3 Solution Providers'); 10 | {{replace_winners}} 11 | console.log(''); 12 | console.log(''); 13 | -------------------------------------------------------------------------------- /template/docs/quick-start.md: -------------------------------------------------------------------------------- 1 | # Quick Start 2 | 3 | Rapidash doesn't need anything other than itself. 4 | 5 |

6 | You can use it for both NodeJs and Browser environment. 7 |

8 | 9 | ## Install 10 | - npm 11 | ```bash 12 | npm install rapidash 13 | ``` 14 | 15 | - yarn 16 | ```bash 17 | yarn add rapidash 18 | ``` 19 | 20 | 21 | ## Usage 22 | ```js 23 | const r = require('rapidash'); 24 | console.log(r.max([1,2,3])); // 3 25 | ``` 26 | 27 |

28 | We will be releasing ES Module soon for better tree shaking. Current library target is UMD. 29 |

30 | 31 | -------------------------------------------------------------------------------- /src/solution-cli.ts: -------------------------------------------------------------------------------- 1 | import * as jest from 'jest'; 2 | import * as path from 'path'; 3 | import { Registry } from './registry'; 4 | 5 | const options = { 6 | projects: [path.join(__dirname, '../')], 7 | } as any; 8 | 9 | if (!process.argv[2]) { 10 | console.log('❌', 'Example Usage: npm run solution max'); 11 | process.exit(0); 12 | } 13 | 14 | if (process.argv[2]) { 15 | console.log('❓', 'Solution Name:', process.argv[2]); 16 | } 17 | 18 | if (process.argv[3]) { 19 | console.log('🙋', 'Owner', process.argv[3]); 20 | } else { 21 | console.log( 22 | '💡', 23 | 'Add second argument solution owner name for testing only your solution' 24 | ); 25 | console.log('💡', 'Example: npm run solution max acanguven'); 26 | } 27 | 28 | console.log(''); 29 | console.log(''); 30 | 31 | jest.runCLI(options, options.projects).then(success => { 32 | if (success.results.numFailedTests === 0) { 33 | Registry.benchmarkWinners(); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /template/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rapidash - Documentation 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 |
15 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ahmet Can Güven 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/functions/index.ts: -------------------------------------------------------------------------------- 1 | import { solution as max } from './max'; 2 | import { solution as factorial } from './factorial'; 3 | import { solution as fibonacci } from './fibonacci'; 4 | import { solution as average } from './average'; 5 | import { solution as isPalindrome } from './is-palindrome'; 6 | import { solution as primes } from './primes'; 7 | import { solution as times } from './times'; 8 | import { solution as equal } from './equal'; 9 | import { solution as isString } from './is-string'; 10 | import { solution as isObject } from './is-object'; 11 | import { solution as isArray } from './is-array'; 12 | import { solution as isNumber } from './is-number'; 13 | import { solution as flatten } from './flatten'; 14 | import { solution as isPrime } from './is-prime'; 15 | import { solution as isNull } from './is-null'; 16 | import { solution as isUndefined } from './is-undefined'; 17 | import { solution as isEmptyArray } from './is-empty-array'; 18 | 19 | export { 20 | max, 21 | factorial, 22 | fibonacci, 23 | average, 24 | isPalindrome, 25 | primes, 26 | times, 27 | equal, 28 | isString, 29 | isObject, 30 | isArray, 31 | isNumber, 32 | flatten, 33 | isPrime, 34 | isNull, 35 | isUndefined, 36 | isEmptyArray, 37 | }; 38 | -------------------------------------------------------------------------------- /src/functions/is-object.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (input: any) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isObject'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('checks the input is object') 13 | .example('r.isObject({}) // true') 14 | .example('r.isObject(2) // false') 15 | .example('r.isObject("") // false') 16 | .example('r.isObject(NaN) // false') 17 | .example('r.isObject([]) // false') 18 | .example('r.isObject(undefined) // false') 19 | .test('Return true when is object', [{}], true) 20 | .test('Return false when is number', [2], false) 21 | .test('Return false when is string', [''], false) 22 | .test('Return false when is NaN', [NaN], false) 23 | .test('Return false when is array', [[]], false) 24 | .test('Return false when is undefined', [undefined], false) 25 | .test('Return false when is date', [new Date()], false) 26 | .bench([{}]); 27 | 28 | /** 29 | * Solutions 30 | * Provide your solutions below 31 | */ 32 | 33 | solution 34 | .owner('cagataycali') 35 | .method('Check input is object') 36 | .fn( 37 | (input: any) => Object.prototype.toString.call(input) === '[object Object]' 38 | ); 39 | -------------------------------------------------------------------------------- /template/docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | 5 |

6 | 7 | # Rapidash 8 | [![CircleCI](https://circleci.com/gh/Acanguven/rapidash/tree/master.svg?style=svg&circle-token=cc706fdf77382859bca066d69dd4003b42251653)](https://circleci.com/gh/Acanguven/rapidash/tree/master) 9 | [![npm version](https://badge.fury.io/js/rapidash.svg)](https://badge.fury.io/js/rapidash) 10 | [![codecov](https://codecov.io/gh/Acanguven/rapidash/branch/master/graph/badge.svg?token=RWcvIRl77k)](https://codecov.io/gh/Acanguven/rapidash) 11 | [![trends](https://img.shields.io/npm/dm/rapidash.svg)](https://www.npmtrends.com/Acanguven/rapidash) 12 | [![size](https://img.shields.io/bundlephobia/minzip/rapidash.svg)](https://bundlephobia.com/result?p=rapidash) 13 | [![pr](https://camo.githubusercontent.com/d4e0f63e9613ee474a7dfdc23c240b9795712c96/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e737667)](https://github.com/Acanguven/rapidash/pulls) 14 | [![pr](https://snyk.io/test/github/Acanguven/rapidash/badge.svg?targetFile=package.json)](https://snyk.io/test/github/Acanguven/rapidash?targetFile=package.json) 15 | 16 | Collection of useful javascript snippets with automated benchmarks! 17 | 18 | [Get Started](#introduction) 19 | -------------------------------------------------------------------------------- /src/functions/is-null.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (input: any) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isNull'); 6 | 7 | /** 8 | * Solution Definition 9 | * Return true if input is null or undefined 10 | */ 11 | 12 | solution 13 | .description('checks the input is null or undefined') 14 | .example('r.isNull(null) // true') 15 | .example('r.isNull(undefined) // true') 16 | .example('r.isNull(0) // false') 17 | .example('r.isNull(false) // false') 18 | .example('r.isNull(true) // false') 19 | .example('r.isNull("") // false') 20 | .test('Return true when is null', [null], true) 21 | .test('Return true when is undefined', [undefined], true) 22 | .test('Return false when is number', [0], false) 23 | .test('Return false when is bool', [false], false) 24 | .test('Return false when is bool', [true], false) 25 | .test('Return false when is string', [''], false) 26 | .bench([null]) 27 | .bench([undefined]) 28 | .bench([false]) 29 | .bench([true]) 30 | .bench(['']) 31 | .bench([0]); 32 | 33 | /** 34 | * Solutions 35 | * Provide your solutions below 36 | */ 37 | 38 | solution 39 | .owner('yavuzkoca') 40 | .method('Type coercion comparision') 41 | .fn((input: any) => input == null); 42 | -------------------------------------------------------------------------------- /src/functions/is-undefined.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (input: any) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isUndefined'); 6 | 7 | /** 8 | * Solution Definition 9 | * Return true if input is undefined 10 | */ 11 | 12 | solution 13 | .description('checks the input is undefined') 14 | .example('r.isUndefined(null) // false') 15 | .example('r.isUndefined(undefined) // true') 16 | .example('r.isUndefined(0) // false') 17 | .example('r.isUndefined(false) // false') 18 | .example('r.isUndefined(true) // false') 19 | .example('r.isUndefined("") // false') 20 | .test('Return false when is null', [null], false) 21 | .test('Return true when is undefined', [undefined], true) 22 | .test('Return false when is number', [0], false) 23 | .test('Return false when is bool', [false], false) 24 | .test('Return false when is bool', [true], false) 25 | .test('Return false when is string', [''], false) 26 | .bench([null]) 27 | .bench([undefined]) 28 | .bench([false]) 29 | .bench([true]) 30 | .bench(['']) 31 | .bench([0]); 32 | 33 | /** 34 | * Solutions 35 | * Provide your solutions below 36 | */ 37 | 38 | solution 39 | .owner('ibrahimozdogan') 40 | .method('Typeof control') 41 | .fn((input: any) => typeof input === 'undefined'); 42 | -------------------------------------------------------------------------------- /src/functions/primes.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (num: number) => number[]; 4 | 5 | export const solution = new SolutionBuilder('primes'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('Generates primes up to a given number.') 13 | .example('r.primes(10) // [2,3,5,7]') 14 | .test('Return empty array till valid primes', [1], []) 15 | .test( 16 | 'Return empty array till valid primes when no argument supplied', 17 | [], 18 | [] 19 | ) 20 | .test('Return average with zero', [10], [2, 3, 5, 7]) 21 | .bench([150]); 22 | 23 | /** 24 | * Solutions 25 | * Provide your solutions below 26 | */ 27 | 28 | solution 29 | .owner('acanguven') 30 | .method( 31 | 'Generate an array from 2 to the given number. Use Array.prototype.filter() to filter out the values divisible by any number from 2 to the square root of the provided number.' 32 | ) 33 | .fn(num => { 34 | let arr = Array.from({ length: num - 1 }).map((x, i) => i + 2); 35 | const sqroot = Math.floor(Math.sqrt(num)); 36 | const numsTillSqroot = Array.from({ length: sqroot - 1 }).map( 37 | (x, i) => i + 2 38 | ); 39 | numsTillSqroot.forEach( 40 | x => (arr = arr.filter(y => y % x !== 0 || y === x)) 41 | ); 42 | return arr; 43 | }); 44 | -------------------------------------------------------------------------------- /src/functions/is-array.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (input: any) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isArray'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('checks the input is array') 13 | .example('r.isArray({}) // true') 14 | .example('r.isArray(2) // false') 15 | .example('r.isArray("") // false') 16 | .example('r.isArray(NaN) // false') 17 | .example('r.isArray([]) // false') 18 | .example('r.isArray(undefined) // false') 19 | .test('Return true when is array', [[]], true) 20 | .test('Return false when is number', [2], false) 21 | .test('Return false when is string', [''], false) 22 | .test('Return false when is NaN', [NaN], false) 23 | .test('Return false when is object', [{}], false) 24 | .test('Return false when is undefined', [undefined], false) 25 | .test('Return false when is date', [new Date()], false) 26 | .bench([{}]); 27 | 28 | /** 29 | * Solutions 30 | * Provide your solutions below 31 | */ 32 | 33 | solution 34 | .owner('cagataycali') 35 | .method('Check input is array') 36 | .fn( 37 | (input: any) => Object.prototype.toString.call(input) === '[object Array]' 38 | ); 39 | 40 | solution 41 | .owner('ibrahimozdogan') 42 | .method('Validate the value by using `Array.isArray`') 43 | .fn(Array.isArray); 44 | -------------------------------------------------------------------------------- /src/functions/is-prime.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (num: number) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isPrime'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('Returns true if given number is prime. Otherwise, false') 13 | .example('r.isPrime(2) // true') 14 | .example('r.isPrime(1) // false') 15 | .test('Return false when input is 1', [1], false) 16 | .test('Return true when input is 2', [2], true) 17 | .test('Return false when input is 9', [9], false) 18 | .test('Return true when input is 11', [11], true) 19 | .test('Return true when input is 9973', [9973], true) 20 | .test('Return true when input is 3', [3], true) 21 | .test('Return false when input is 10', [25], false) 22 | .bench([9973]) 23 | .bench([10007]) 24 | .bench([9851]) 25 | .bench([7529]); 26 | 27 | /** 28 | * Solutions 29 | * Provide your solutions below 30 | */ 31 | 32 | solution 33 | .owner('yavuzkoca') 34 | .method('Loop through the square root of the given number') 35 | .fn((num: number): boolean => { 36 | if (num === 1) return false; 37 | if (num === 2) return true; 38 | if (num === 3) return true; 39 | if (num % 3 === 0) return false; 40 | for (let i = 5, width = 2; i * i <= num; i += width, width = 6 - width) { 41 | if (num % i === 0) return false; 42 | } 43 | 44 | return true; 45 | }); 46 | -------------------------------------------------------------------------------- /template/docs/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | 4 | ## What is Rapidash? 5 | ___ 6 | Rapidash is a Javascript utility library designed for performance, modularity, and reliability. Unlike other utility libraries, Rapidash designed from the ground up to be fast all the time against all Javascript engine versions. Also being open-source friendly, it is evolving faster than any other utility library. 7 | 8 |

9 | There is always more than one solution to a problem in software programming. 10 | What makes Rapidash great is picking the best one among tens of solutions. 11 |

12 | 13 | Follow [quick start](quick-start.md) guide! 14 | 15 | ## How it works? 16 | ___ 17 | 1. A new problem released on Rapidash. 18 | 2. Contributors submit solutions. 19 | 3. The new solution is tested automatically and if it is valid it gets added to the solution list. 20 | 3. Rapidash automatically picks the fastest solution on each build so each version shipped with the fastest solutions all the time. 21 | 4. Other solutions are removed from the bundle to reduce bundle size automatically. 22 | 23 |

24 | Rapidash releases new version each sunday picking the fastest solutions from list and updates rankings. 25 |

26 | 27 | 28 | 29 | Current version of Rapidash is: {{currentVersion}} with size {{bundleSize}}kb *without gzip*. 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/solution.ts: -------------------------------------------------------------------------------- 1 | class Solution { 2 | methodDescription!: string; 3 | func!: T; 4 | owner: string; 5 | 6 | constructor(name: string) { 7 | this.owner = name; 8 | } 9 | 10 | method(way: string) { 11 | this.methodDescription = way; 12 | return this; 13 | } 14 | 15 | fn(handler: T) { 16 | this.func = handler; 17 | return this; 18 | } 19 | } 20 | 21 | class SolutionBuilder { 22 | testCases: Array<{ 23 | input: unknown[]; 24 | output: unknown; 25 | name: string; 26 | }> = []; 27 | 28 | solutions: Array> = []; 29 | 30 | name: string; 31 | 32 | benchmarkInput: unknown[][] = []; 33 | 34 | examples: string[] = []; 35 | 36 | descr = ''; 37 | 38 | constructor(name: string) { 39 | this.name = name; 40 | } 41 | 42 | test(name: string, input: unknown[], output: unknown) { 43 | this.testCases.push({ name, input, output }); 44 | return this; 45 | } 46 | 47 | owner(name: string) { 48 | const method = new Solution(name); 49 | this.solutions.push(method); 50 | return method; 51 | } 52 | 53 | bench(input: unknown[]) { 54 | this.benchmarkInput.push(input); 55 | return this; 56 | } 57 | 58 | example(example: string) { 59 | this.examples.push(example); 60 | return this; 61 | } 62 | 63 | description(description: string) { 64 | this.descr = description; 65 | return this; 66 | } 67 | } 68 | 69 | export { Solution, SolutionBuilder }; 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rapidash", 3 | "version": "1.2.1", 4 | "description": "Fastest functional utility library created by NodeJs community", 5 | "main": "build/index.js", 6 | "types": "build/index.d.ts", 7 | "files": [ 8 | "build/src" 9 | ], 10 | "license": "MIT", 11 | "keywords": [ 12 | "javascript", 13 | "utility", 14 | "library" 15 | ], 16 | "scripts": { 17 | "solution": "TS_NODE_FILES=true node -r ts-node/register ./src/solution-cli.ts", 18 | "test": "jest --coverage", 19 | "check": "gts check", 20 | "clean": "gts clean", 21 | "compile": "tsc -p .", 22 | "compile:watch": "tsc -p . --watch", 23 | "fix": "gts fix", 24 | "prepare": "npm run compile", 25 | "pretest": "npm run compile", 26 | "posttest": "npm run check" 27 | }, 28 | "devDependencies": { 29 | "@types/benchmark": "^1.0.31", 30 | "@types/jest": "^25.1.0", 31 | "@types/node": "^10.0.3", 32 | "@types/webpack": "^4.41.3", 33 | "benchmark": "^2.1.4", 34 | "chalk": "^3.0.0", 35 | "cli-table": "^0.3.1", 36 | "gts": "^1.1.2", 37 | "husky": "^4.2.1", 38 | "jest": "^25.1.0", 39 | "jest-cli": "^25.1.0", 40 | "ts-jest": "^25.0.0", 41 | "ts-node": "^8.6.2", 42 | "typescript": "~3.7.0", 43 | "webpack": "^4.41.5", 44 | "webpack-cli": "^3.3.10", 45 | "@types/cli-table": "^0.3.0" 46 | }, 47 | "husky": { 48 | "hooks": { 49 | "pre-commit": "npm run test" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/functions/is-string.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (input: any) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isString'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('checks the input is string') 13 | .example('r.isString("") // true') 14 | .example('r.isString({}) // false') 15 | .example('r.isString(NaN) // false') 16 | .example('r.isString(2) // false') 17 | .example('r.isString([]) // false') 18 | .example('r.isString(undefined) // false') 19 | .test('Return true when is string', [''], true) 20 | .test('Return false when is object', [{}], false) 21 | .test('Return false when is NaN', [NaN], false) 22 | .test('Return false when is number', [2], false) 23 | .test('Return false when is array', [[]], false) 24 | .test('Return false when is undefined', [undefined], false) 25 | .test('Return false when is date', [new Date()], false) 26 | .bench([{}]) 27 | .bench([]) 28 | .bench([2]) 29 | .bench([[]]) 30 | .bench([NaN]) 31 | .bench(['']); 32 | /** 33 | * Solutions 34 | * Provide your solutions below 35 | */ 36 | 37 | solution 38 | .owner('cagataycali') 39 | .method('Check input is string') 40 | .fn( 41 | (input: any) => Object.prototype.toString.call(input) === '[object String]' 42 | ); 43 | 44 | solution 45 | .owner('yavuzkoca') 46 | .method('JS Typeof') 47 | .fn((input: any) => typeof input === 'string'); 48 | -------------------------------------------------------------------------------- /src/functions/average.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (...arr: number[]) => number; 4 | 5 | export const solution = new SolutionBuilder('average'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('Returns the average of two or more numbers.') 13 | .example('r.average() // NaN') 14 | .example('r.average(1) // 1') 15 | .example('r.average(1,2) // 1.5') 16 | .example('r.average(1,2,3) // 2') 17 | .example('r.average(1,2,3,0) // 1.5') 18 | .test('Return nan when no parameter', [], NaN) 19 | .test('Return same when single parameter', [55], 55) 20 | .test('Return average', [1, 2, 3], 2) 21 | .test('Return average with zero', [1, 2, 3, 0], 1.5) 22 | .bench([1, 2, 3, 0, 73, 53, 23, 54, 23, 74, 3, 42]); 23 | 24 | /** 25 | * Solutions 26 | * Provide your solutions below 27 | */ 28 | 29 | solution 30 | .owner('acanguven') 31 | .method( 32 | 'Use Array.prototype.reduce() to add each value to an accumulator, initialized with a value of 0, divide by the length of the array.' 33 | ) 34 | .fn((...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length); 35 | 36 | solution 37 | .owner('ibrahimozdogan') 38 | .method('Uses `for loop` in order to calculate average of the given numbers') 39 | .fn((...nums) => { 40 | const length = nums.length; 41 | let sum = nums[0]; 42 | 43 | for (let i = 1; i < length; i++) { 44 | sum += nums[i]; 45 | } 46 | 47 | return sum / length; 48 | }); 49 | -------------------------------------------------------------------------------- /src/functions/fibonacci.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (num: number) => number; 4 | 5 | export const solution = new SolutionBuilder('fibonacci'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('Calculates the fibonacci number with given index.') 13 | .example('r.fibonacci(1) // 1') 14 | .example('r.fibonacci(3) // 3') 15 | .example('r.fibonacci(20) // 6765') 16 | .test('Return fibonacci for 1', [1], 1) 17 | .test('Return fibonacci for 3', [3], 2) 18 | .test('Return fibonacci for 20', [20], 6765) 19 | .test('Return fibonacci for 70', [70], 190392490709135) 20 | .test('Return fibonacci for 90', [90], 2880067194370816000) 21 | .bench([14]) 22 | .bench([100]); 23 | // .bench([200]) 24 | // .bench([1000]); 25 | 26 | /** 27 | * Solutions 28 | * Provide your solutions below 29 | */ 30 | 31 | solution 32 | .owner('yavuzkoca') 33 | .method('Dynamic') 34 | .fn(function fibonacci(n): number { 35 | let b = 1; 36 | for (let i = 2, a = 0, c; i <= n; ++i) { 37 | c = a + b; 38 | a = b; 39 | b = c; 40 | } 41 | 42 | return b; 43 | }); 44 | 45 | solution 46 | .owner('Emre-Kul') 47 | .method('Golden Ratio') 48 | .fn(function fibonacci(n): number { 49 | if (n <= 1) return 1; 50 | if (n > 70) { 51 | let b = 1; 52 | for (let i = 2, a = 0, c; i <= n; ++i) { 53 | c = a + b; 54 | a = b; 55 | b = c; 56 | } 57 | return b; 58 | } 59 | const sq5 = Math.sqrt(5); 60 | const phi1 = (1 + sq5) / 2; 61 | const phi2 = (1 - sq5) / 2; 62 | return Math.round(Math.pow(phi1, n) / sq5 + Math.pow(phi2, n) / sq5); 63 | }); 64 | -------------------------------------------------------------------------------- /src/functions/is-number.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (input: any) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isNumber'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('checks the input is number') 13 | .example('r.isNumber(2) // true') 14 | .example('r.isNumber("") // false') 15 | .example('r.isNumber({}) // false') 16 | .example('r.isNumber(NaN) // false') 17 | .example('r.isNumber([]) // false') 18 | .example('r.isNumber(undefined) // false') 19 | .test('Return true when is number', [2], true) 20 | .test('Return false when is string', [''], false) 21 | .test('Return false when is object', [{}], false) 22 | .test('Return false when is NaN', [NaN], false) 23 | .test('Return false when is array', [[]], false) 24 | .test('Return false when is undefined', [undefined], false) 25 | .test('Return false when is date', [new Date()], false) 26 | .bench([2]) 27 | .bench(['']) 28 | .bench([{}]) 29 | .bench([NaN]) 30 | .bench([undefined]) 31 | .bench([[]]); 32 | 33 | /** 34 | * Solutions 35 | * Provide your solutions below 36 | */ 37 | 38 | solution 39 | .owner('cagataycali') 40 | .method('Check input is number') 41 | .fn( 42 | (input: any) => 43 | !isNaN(input) && 44 | Object.prototype.toString.call(input) === '[object Number]' 45 | ); 46 | 47 | solution 48 | .owner('yavuzkoca') 49 | .method('JS Typeof') 50 | .fn((input: any) => !isNaN(input) && typeof input === 'number'); 51 | 52 | solution 53 | .owner('ibrahimozdogan') 54 | .method('Validate value by using short-circuit evaluation and typeof') 55 | .fn((value: any) => typeof (value || '') === 'number'); 56 | -------------------------------------------------------------------------------- /src/functions/times.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (times: number, data: any) => any | undefined; 4 | 5 | export const solution = new SolutionBuilder('times'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description( 13 | 'times receives as arguments the number of iterations and a function to execute n times and returns an array of the results. Very useful when creating dynamic test data.' 14 | ) 15 | .example('r.times(2, null) // [null, null]') 16 | .example('r.times() // []') 17 | .example('r.times(2) // [undefined, undefined]') 18 | .example('r.times(2, 2) // [2, 2]') 19 | .test('Return filled null twice', [2, null], [null, null]) 20 | .test('Return filled undefined twice', [2], [undefined, undefined]) 21 | .test( 22 | 'Return filled object twice', 23 | [2, { test: 'object' }], 24 | [{ test: 'object' }, { test: 'object' }] 25 | ) 26 | .test('Return filled 2 twice', [2, 2], [2, 2]) 27 | .bench([2, 2]) 28 | .bench([100, { hello: 'world' }]) 29 | .bench([ 30 | 1000, 31 | { hello: 'world', nested: { object: { with: { array: [] } } } }, 32 | ]); 33 | 34 | /** 35 | * Solutions 36 | * Provide your solutions below 37 | */ 38 | 39 | solution 40 | .owner('cagataycali') 41 | .method('Using new Arrray().fill.') 42 | .fn((times, data) => { 43 | return new Array(times).fill(data); 44 | }); 45 | 46 | solution 47 | .owner('ibrahimozdogan') 48 | .method( 49 | 'Filling array according to the `times` with the `value` by using `for` loop' 50 | ) 51 | .fn((times, value) => { 52 | const data = []; 53 | 54 | for (let i = 0; i < times; i++) { 55 | data.push(value); 56 | } 57 | 58 | return data; 59 | }); 60 | -------------------------------------------------------------------------------- /src/functions/is-empty-array.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (input: any) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isEmptyArray'); 6 | 7 | /** 8 | * Solution Definition 9 | * Return true if input is an empty array. if the input is not an array, returns `false` 10 | */ 11 | 12 | solution 13 | .description('checks the input is empty array') 14 | .example('r.isEmptyArray(null) // false') 15 | .example('r.isEmptyArray(undefined) // false') 16 | .example('r.isEmptyArray({}) // false') 17 | .example('r.isEmptyArray([]) // true') 18 | .example('r.isEmptyArray([""]) // false') 19 | .example('r.isEmptyArray("test") // false') 20 | .example('r.isEmptyArray("") // false') 21 | .example('r.isEmptyArray(1) // false') 22 | .example('r.isEmptyArray([new Date()]) // false') 23 | .test('Return false when is null', [null], false) 24 | .test('Return false when is undefined', [undefined], false) 25 | .test('Return false when is number', [0], false) 26 | .test('Return false when is bool', [false], false) 27 | .test('Return false when is bool', [true], false) 28 | .test('Return false when is string', [''], false) 29 | .test('Return true when is empty array', [[]], true) 30 | .test('Return false when is not empty array', [['']], false) 31 | .bench([null]) 32 | .bench([undefined]) 33 | .bench([false]) 34 | .bench([true]) 35 | .bench(['']) 36 | .bench([]) 37 | .bench([[], []]) 38 | .bench([['']]) 39 | .bench([{}]) 40 | .bench([0]); 41 | 42 | /** 43 | * Solutions 44 | * Provide your solutions below 45 | */ 46 | 47 | solution 48 | .owner('ibrahimozdogan') 49 | .method('Checks the given is empty array or not') 50 | .fn((value: any) => Array.isArray(value) && value.length === 0); 51 | -------------------------------------------------------------------------------- /src/functions/flatten.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (array: T[], depth?: number) => T[]; 4 | 5 | export const solution = new SolutionBuilder('flatten'); 6 | 7 | solution 8 | .description('Flattens given array according to given depth.') 9 | .example('r.flatten([2, 3, [4, [6]]], 1) // [2, 3, 4, [6]]') 10 | .example('r.flatten([2, 3, [4, [6]]], 2) // [2, 3, 4, 6]') 11 | .test('Return empty array', [null], []) 12 | .test('Return same array', [[1, 2], 1], [1, 2]) 13 | .test('Return flattened array for depth 1', [[1, [3]], 1], [1, 3]) 14 | .test( 15 | 'Return flattened array for depth 2', 16 | [[1, [3, [4, 5]]], 2], 17 | [1, 3, 4, 5] 18 | ) 19 | .bench([[1, 2, [2, 3, 4, [3, 4, [5]]]], 1]); 20 | solution 21 | .owner('mehmetsefabalik') 22 | .method('Flatten') 23 | // tsc complaint about 'implicitly any type' for depth param when I removed the number type, 24 | // so I should disable this rule for next line 25 | // tslint:disable-next-line: no-inferrable-types 26 | .fn((array: T[], depth: number = 1): T[] => { 27 | const isFlattenable = (value: any) => { 28 | return ( 29 | Array.isArray(value) || !!(value && value[Symbol.isConcatSpreadable]) 30 | ); 31 | }; 32 | const base = (array: any, depth: number, result: any[]): any => { 33 | if (array === null || array === undefined) { 34 | return result; 35 | } 36 | 37 | for (let index = 0; index < array.length; index++) { 38 | const value = array[index]; 39 | if (depth > 0 && isFlattenable(value)) { 40 | if (depth > 1) { 41 | base(value, depth - 1, result); 42 | } else { 43 | result.push(...(value as [])); 44 | } 45 | } else { 46 | result[result.length] = value; 47 | } 48 | } 49 | 50 | return result; 51 | }; 52 | return base(array, depth, []); 53 | }); 54 | -------------------------------------------------------------------------------- /src/functions/is-palindrome.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (word: string) => boolean; 4 | 5 | export const solution = new SolutionBuilder('isPalindrome'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('Returns true if given word is palindrome. Otherwise, false') 13 | .example('r.isPalindrome("") // true') 14 | .example('r.isPalindrome("a") // true') 15 | .example('r.isPalindrome("aa") // true') 16 | .example('r.isPalindrome("aab") // false') 17 | .example('r.isPalindrome("aaaaa") // true') 18 | .example('r.isPalindrome("aaaaabaaa") // false') 19 | .test('Return true when input is empty string', [''], true) 20 | .test('Return true when input is one character', ['a'], true) 21 | .test('Return true when input is two character', ['aa'], true) 22 | .test('Return false when input is "aab"', ['aab'], false) 23 | .test('Return true when input is "aaaaa"', ['aaaaa'], true) 24 | .test('Return false when input is "aaaaabaaa"', ['aaaaabaaa'], false) 25 | .test( 26 | 'Return false when input is not palindrome', 27 | [`${'b'.repeat(1000)}${'a'.repeat(1000)}`], 28 | false 29 | ) 30 | .test( 31 | 'Return false when input is not palindrome', 32 | [`${'a'.repeat(1000)}ab${'a'.repeat(1000)}`], 33 | false 34 | ) 35 | .test('Return true when input is palindrome', [`${'a'.repeat(1000)}`], true) 36 | .bench([`${'a'.repeat(10)}b${'a'.repeat(9)}`]) 37 | .bench(['a'.repeat(1000)]) 38 | .bench([`${'a'.repeat(1000)}ab${'a'.repeat(1000)}`]) 39 | .bench([`${'b'.repeat(1000)}${'a'.repeat(1000)}`]); 40 | 41 | /** 42 | * Solutions 43 | * Provide your solutions below 44 | */ 45 | 46 | solution 47 | .owner('yavuzkoca') 48 | .method('Loop to the half of the string and compare character by character') 49 | .fn((word: string): boolean => { 50 | const len = word.length; 51 | for (let i = 0; i < len / 2; i++) { 52 | if (word[i] !== word[len - i - 1]) return false; 53 | } 54 | 55 | return true; 56 | }); 57 | -------------------------------------------------------------------------------- /src/functions/factorial.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (num: number) => number; 4 | 5 | export const solution = new SolutionBuilder('factorial'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description('Calculates the factorial of a number.') 13 | .example('r.factorial(6) // 720') 14 | .test('Return factorial', [6], 720) 15 | .test('Return 1 for input 0', [0], 1) 16 | .bench([14]) 17 | .bench([3]) 18 | .bench([60]); 19 | 20 | /** 21 | * Solutions 22 | * Provide your solutions below 23 | */ 24 | 25 | solution 26 | .owner('acanguven') 27 | .method('Recursive') 28 | .fn(function factorial(n): number { 29 | return n <= 1 ? 1 : factorial(n - 1) * n; 30 | }); 31 | 32 | solution 33 | .owner('BatuAksoy') 34 | .method('Tail Recursive') 35 | .fn(function factorial(n, accumulator = 1): number { 36 | return n <= 1 ? accumulator : factorial(n - 1, n * accumulator); 37 | }); 38 | 39 | solution 40 | .owner('yavuzkoca') 41 | .method('For Loop') 42 | .fn(function factorial(n): number { 43 | let b = 1; 44 | for (let i = 2; i <= n; i++) { 45 | b *= i; 46 | } 47 | 48 | return b; 49 | }); 50 | 51 | solution 52 | .owner('eylmz') 53 | .method('Reverse For Loop') 54 | .fn(function factorial(n): number { 55 | let b = 1; 56 | for (let i = n; i >= 2; i--) { 57 | b *= i; 58 | } 59 | 60 | return b; 61 | }); 62 | 63 | solution 64 | .owner('ilker0') 65 | .method('While Loop') 66 | .fn(function factorial(n): number { 67 | let b = 1; 68 | while (n > 0) { 69 | b *= n; 70 | n -= 1; 71 | } 72 | 73 | return b; 74 | }); 75 | 76 | solution 77 | .owner('ibrahimozdogan') 78 | .method('Factorial of given value by using tabulation caching technique') 79 | .fn( 80 | ((cache: { [key: number]: number }) => { 81 | return (value: number) => { 82 | if (cache[value]) { 83 | return cache[value]; 84 | } 85 | 86 | let b = 1; 87 | for (let i = value; i >= 2; i--) { 88 | b *= i; 89 | cache[i] = b; 90 | } 91 | 92 | return b; 93 | }; 94 | })({ 0: 1, 1: 1 }) 95 | ); 96 | -------------------------------------------------------------------------------- /src/registry.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from './solution'; 2 | import { Benchmark, BenchmarkReport } from './benchmark'; 3 | import * as solutions from './functions'; 4 | 5 | type BenchmarkResults = Array<{ 6 | function: SolutionBuilder; 7 | benchmarkResults: BenchmarkReport; 8 | }>; 9 | 10 | const solutionArg = 11 | process.argv[2] === '--coverage' ? undefined : process.argv[2]; 12 | const ownerArg = process.argv[3]; 13 | 14 | class Registry { 15 | static solutions: SolutionBuilder[] = []; 16 | 17 | static solution(fnName: string, solutions: SolutionBuilder) { 18 | this.solutions.push(solutions); 19 | return this; 20 | } 21 | 22 | static async benchmarkWinners() { 23 | const benchMarksResults: BenchmarkResults = []; 24 | 25 | for (const solution of this.solutions) { 26 | if (!solutionArg || solution.name === solutionArg) { 27 | const benchmark = new Benchmark( 28 | solution.solutions, 29 | solution.benchmarkInput, 30 | solution.name 31 | ); 32 | 33 | benchMarksResults.push({ 34 | function: solution, 35 | benchmarkResults: await benchmark.run(), 36 | }); 37 | } 38 | } 39 | 40 | return benchMarksResults; 41 | } 42 | 43 | static runSolutionTests() { 44 | this.solutions.forEach(solutionBuilder => { 45 | if (!solutionArg || solutionArg === solutionBuilder.name) { 46 | describe(solutionBuilder.name, () => { 47 | solutionBuilder.solutions.forEach(solution => { 48 | if (!ownerArg || ownerArg === solution.owner) { 49 | describe(`Solution Owner: ${solution.owner}, method: ${solution.methodDescription}`, () => { 50 | solutionBuilder.testCases.forEach(test => { 51 | it(test.name, () => { 52 | const returnValue = (solution.func as any)!(...test.input); 53 | expect(returnValue).toEqual(test.output); 54 | }); 55 | }); 56 | }); 57 | } 58 | }); 59 | }); 60 | } 61 | }); 62 | } 63 | } 64 | 65 | Object.keys(solutions).forEach(key => { 66 | Registry.solution(key, (solutions as any)[key]); 67 | }); 68 | 69 | export { BenchmarkResults, Registry }; 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # TypeScript v1 declaration files 46 | typings/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Microbundle cache 58 | .rpt2_cache/ 59 | .rts2_cache_cjs/ 60 | .rts2_cache_es/ 61 | .rts2_cache_umd/ 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | 66 | # Output of 'npm pack' 67 | *.tgz 68 | 69 | # Yarn Integrity file 70 | .yarn-integrity 71 | 72 | # dotenv environment variables file 73 | .env 74 | .env.test 75 | 76 | # parcel-bundler cache (https://parceljs.org/) 77 | .cache 78 | 79 | # Next.js build output 80 | .next 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | 107 | build/ 108 | pre-build/ 109 | dist/ 110 | docs/ 111 | !template/docs 112 | 113 | 114 | .idea 115 | yarn.lock 116 | 117 | -------------------------------------------------------------------------------- /src/functions/max.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (arr: number[]) => number | undefined; 4 | 5 | export const solution = new SolutionBuilder('max'); 6 | 7 | /** 8 | * Solution Definition 9 | */ 10 | 11 | solution 12 | .description( 13 | 'Computes the maximum value of array. If array is empty or falsy, undefined is returned.' 14 | ) 15 | .example('r.max([1,2,3]) // 3') 16 | .example('r.max() // undefined') 17 | .example('r.max([]) // undefined') 18 | .test('Return max number', [[1, 2, 3]], 3) 19 | .test('Return max number', [[3, 1, 6, 3, 7]], 7) 20 | .test('Return undefined for empty', [[]], undefined) 21 | .test('Return undefined for no params', [], undefined) 22 | .bench([ 23 | [ 24 | 1, 25 | 2, 26 | 3, 27 | 4, 28 | 5, 29 | 6, 30 | 1, 31 | 3, 32 | 5, 33 | 7, 34 | 2, 35 | 8, 36 | 9, 37 | 32, 38 | 4, 39 | 8, 40 | 4, 41 | 4, 42 | 5, 43 | 7, 44 | 56, 45 | 1, 46 | 5, 47 | 8, 48 | 4, 49 | 4, 50 | 6, 51 | ], 52 | ]); 53 | 54 | /** 55 | * Solutions 56 | * Provide your solutions below 57 | */ 58 | 59 | solution 60 | .owner('acanguven') 61 | .method('Reduce') 62 | .fn(arr => { 63 | if (!arr || !arr.length) return; 64 | 65 | return arr.reduce((a, b) => { 66 | return Math.max(a, b); 67 | }); 68 | }); 69 | 70 | solution 71 | .owner('cagataycali') 72 | .method('Reduce') 73 | .fn(arr => { 74 | if (!arr || !arr.length) return; 75 | 76 | return arr.reduce((max, val) => (val > max ? val : max), arr[0]); 77 | }); 78 | 79 | solution 80 | .owner('yavuzkoca') 81 | .method('Linear') 82 | .fn(arr => { 83 | if (!arr) return; 84 | const len = arr.length; 85 | if (!len) return; 86 | let max = arr[0]; 87 | 88 | for (let i = 1; i < len; ++i) { 89 | if (arr[i] > max) max = arr[i]; 90 | } 91 | 92 | return max; 93 | }); 94 | 95 | solution 96 | .owner('ilker0') 97 | .method('Linear') 98 | .fn(arr => { 99 | if (!arr || !arr.length) return; 100 | 101 | let max = arr[0]; 102 | 103 | for (let i = 0, len = arr.length; i < len; ++i) { 104 | if (arr[i] > max) max = arr[i]; 105 | } 106 | 107 | return max; 108 | }); 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Rapidash 6 | [![CircleCI](https://circleci.com/gh/Acanguven/rapidash/tree/master.svg?style=svg&circle-token=cc706fdf77382859bca066d69dd4003b42251653)](https://circleci.com/gh/Acanguven/rapidash/tree/master) 7 | [![npm version](https://badge.fury.io/js/rapidash.svg)](https://badge.fury.io/js/rapidash) 8 | [![codecov](https://codecov.io/gh/Acanguven/rapidash/branch/master/graph/badge.svg?token=RWcvIRl77k)](https://codecov.io/gh/Acanguven/rapidash) 9 | [![trends](https://img.shields.io/npm/dm/rapidash.svg)](https://www.npmtrends.com/Acanguven/rapidash) 10 | [![size](https://img.shields.io/bundlephobia/minzip/rapidash.svg)](https://bundlephobia.com/result?p=rapidash) 11 | [![pr](https://camo.githubusercontent.com/d4e0f63e9613ee474a7dfdc23c240b9795712c96/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e737667)](https://github.com/Acanguven/rapidash/pulls) 12 | [![pr](https://snyk.io/test/github/Acanguven/rapidash/badge.svg?targetFile=package.json)](https://snyk.io/test/github/Acanguven/rapidash?targetFile=package.json) 13 | 14 | Follow the [documentation](https://acg.software/rapidash/) for more information about Rapidash and get a quick start! 15 | 16 | ## What is Rapidash? 17 | Rapidash is a JavaScript utility library designed for performance, modularity, and reliability. Unlike other utility libraries, Rapidash designed from the ground up to be fast all the time against all JavaScript engine versions. Also being open-source friendly, it is evolving faster than any other utility library. 18 | 19 | 20 | ## Install 21 | - npm 22 | ```bash 23 | npm install rapidash 24 | ``` 25 | 26 | - yarn 27 | ```bash 28 | yarn add rapidash 29 | ``` 30 | 31 | 32 | ## Usage 33 | ```js 34 | const r = require('rapidash'); 35 | console.log(r.max([1,2,3])); // 3 36 | ``` 37 | 38 | Check out [functions](https://acg.software/rapidash/#/functions) for all features. 39 | 40 | 41 | ## How it works? 42 | 1. A new problem released on Rapidash. 43 | 2. Contributors submit solutions. 44 | 3. The new solution is tested automatically and if it is valid it gets added to the solution list. 45 | 3. Rapidash automatically picks the fastest solution on each build so each version shipped with the fastest solutions all the time. 46 | 4. Other solutions are removed from the bundle to reduce bundle size automatically. 47 | 48 | ## Why? 49 | Because creating high-quality pull requests is hard. Just because of this lots of newbies are not even trying to contribute open-source projects. But with Rapidash every solution that passes tests is accepted. You will also get scored based on your solutions ops/sec. With a little bit of gamification, I believe we can courage people into more open-source projects. 50 | -------------------------------------------------------------------------------- /template/docs/submit-a-solution.md: -------------------------------------------------------------------------------- 1 | # Submitting a Solution 2 | 3 | Rapidash is open-source friendly, feel free to submit any solution that passes the tests. All you need to create a pull request. 4 | 5 | ## Step by Step Guide 6 | 7 | > /src/functions/factorial.ts 8 | 9 | ```js 10 | import {SolutionBuilder} from '../solution'; 11 | 12 | export type definition = (num: number) => number; 13 | 14 | export const solution = new SolutionBuilder('factorial'); 15 | 16 | /** 17 | * Solution Definition 18 | */ 19 | 20 | solution 21 | .description('Calculates the factorial of a number.') 22 | .example('r.factorial(6) // 720') 23 | .test('Return factorial', [6], 720) 24 | .bench([14]); 25 | 26 | /** 27 | * Solutions 28 | * Provide your totalSolutions below 29 | */ 30 | 31 | solution 32 | .owner('acanguven') 33 | .method('Recursive') 34 | .fn(function factorial(n): number { 35 | return n <= 1 ? 1 : factorial(n - 1) * n 36 | }); 37 | ``` 38 | 39 | We have a `factorial` method that calculates the factorial of a number. Currently, there is only one solution submitted. 40 | Now we will be adding another solution. Check out the template below. 41 | 42 | ```js 43 | solution 44 | .owner('your github username') 45 | .method('which method you used to solve the problem') 46 | .fn(function factorial(n) { 47 | //the solution implementation 48 | return n <= 1 ? 1 : factorial(n - 1) * n 49 | }); 50 | ``` 51 | 52 |

53 | Project is built on typescript so static type checking is enabled to provide better functional programming experience. 54 |

55 | 56 | 57 | After adding our new solution, the latest version of the file will be like below. 58 | 59 | ```js 60 | import {SolutionBuilder} from '../solution'; 61 | 62 | export type definition = (num: number) => number; 63 | 64 | export const solution = new SolutionBuilder('factorial'); 65 | 66 | /** 67 | * Solution Definition 68 | */ 69 | 70 | solution 71 | .description('Calculates the factorial of a number.') 72 | .example('r.factorial(6) // 720') 73 | .test('Return factorial', [6], 720) 74 | .bench([14]); 75 | 76 | /** 77 | * Solutions 78 | * Provide your totalSolutions below 79 | */ 80 | 81 | solution 82 | .owner('acanguven') 83 | .method('Recursive') 84 | .fn(function factorial(n): number { 85 | return n <= 1 ? 1 : factorial(n - 1) * n 86 | }); 87 | 88 | solution 89 | .owner('mygithubusername') 90 | .method('byloopinglikeaboss') 91 | .fn(function factorial(n) { 92 | //the solution implementation 93 | return n <= 1 ? 1 : factorial(n - 1) * n 94 | }); 95 | ``` 96 | 97 | Then open a pull request, tests will automatically run and if they pass we will merge your solution and you will get your score. 98 | 99 | Tests are located above the file: `.test('Test name', [param1, param2], expectedOutput)`. There can be multiple tests. You can run `npm run test` to run tests locally. 100 | 101 | ## Running Benchmarks & Tests 102 | 103 | You can run tests and benchmarks on your computer too. It is useful for develop - benchmark cycle. 104 | 105 | * Example Usage 106 | ```bash 107 | npm run solution max acanguven 108 | ``` 109 | 110 | Rapidash runs tests for acanguven's max solution then starts benchmarking it comparing to other solutions. The owner's name is optional for tests targeting other solutions. 111 | -------------------------------------------------------------------------------- /src/benchmark.ts: -------------------------------------------------------------------------------- 1 | import { Suite } from 'benchmark'; 2 | import { Solution } from './solution'; 3 | 4 | const cliTable = require('cli-table'); 5 | 6 | interface BenchmarkReport { 7 | winnerSolution: { 8 | owner: string; 9 | hz: number; 10 | }; 11 | benchmarks: Array<{ 12 | text: string; 13 | opsSec: number; 14 | owner: string; 15 | }>; 16 | } 17 | 18 | class Benchmark { 19 | private solutions: Solution[]; 20 | private suite: Suite; 21 | private benchParams: unknown[][]; 22 | private name: string; 23 | 24 | constructor(solutions: Solution[], benchParams: unknown[][], name: string) { 25 | this.solutions = solutions; 26 | this.suite = new Suite(); 27 | this.benchParams = benchParams; 28 | this.name = name; 29 | } 30 | 31 | async run(): Promise { 32 | let defer: (winner: BenchmarkReport) => void; 33 | this.solutions.forEach(solution => { 34 | const benchFunctions = this.benchParams.map(params => ({ 35 | fn: solution.func.bind(null, ...params), 36 | case: `${this.name}(${JSON.stringify(params).slice(1, -1)})`, 37 | })); 38 | 39 | benchFunctions.forEach(bench => { 40 | this.suite.add(`${solution.owner} - ${bench.case}`, () => { 41 | bench.fn(); 42 | }); 43 | }); 44 | }); 45 | 46 | this.suite.on('complete', () => { 47 | const fastest = this.suite.filter('fastest'); 48 | 49 | const table = new cliTable({ 50 | head: ['Solution Owner', 'Test Case', 'ops/sec'], 51 | }); 52 | 53 | this.suite.forEach((bench: any) => { 54 | const [owner, testCase] = bench.name.split(' - '); 55 | table.push([owner, testCase, Math.round(bench.hz).toLocaleString()]); 56 | }); 57 | 58 | const benchAverages = this.suite 59 | .map((bench: any) => { 60 | const [owner] = bench.name.split(' - '); 61 | const totalTests = this.suite.filter((suite: any) => 62 | suite.name.startsWith(`${owner} -`) 63 | ); 64 | const hz = 65 | totalTests.reduce((avg: number, c: any) => avg + c.hz, 0) / 66 | totalTests.length; 67 | 68 | return { 69 | name: owner, 70 | hz, 71 | text: Math.round(hz).toLocaleString(), 72 | }; 73 | }) 74 | .sort((a, b) => b.hz - a.hz); 75 | 76 | console.log(table.toString()); 77 | console.log( 78 | '🏆', 79 | `Fastest solution owner: ${benchAverages[0].name} with average ops/sec ${benchAverages[0].text}` 80 | ); 81 | console.log(''); 82 | console.log(''); 83 | console.log(''); 84 | 85 | defer({ 86 | winnerSolution: { 87 | owner: benchAverages[0].name, 88 | hz: benchAverages[0].hz, 89 | }, 90 | benchmarks: benchAverages.map((bench: any) => ({ 91 | text: bench.text, 92 | opsSec: bench.hz, 93 | owner: bench.name, 94 | })), 95 | }); 96 | }); 97 | 98 | this.suite.on('cycle', (event: any) => { 99 | console.log('⚡', event.target.toString()); 100 | }); 101 | 102 | return new Promise(resolve => { 103 | defer = resolve; 104 | console.log('🏎️', ` Starting benchmarks for ${this.name}`); 105 | this.suite.run({ async: true }); 106 | }); 107 | } 108 | } 109 | 110 | export { Benchmark, BenchmarkReport }; 111 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | 4 | defaults: &defaults 5 | docker: 6 | - image: circleci/node:10 7 | 8 | orbs: 9 | codecov: codecov/codecov@1.0.5 10 | 11 | jobs: 12 | test: 13 | <<: *defaults 14 | steps: 15 | - checkout 16 | - restore_cache: 17 | keys: 18 | - v1-dependencies-{{ checksum "package.json" }} 19 | - run: npm i 20 | - save_cache: 21 | paths: 22 | - node_modules 23 | key: v1-dependencies-{{ checksum "package.json" }} 24 | - run: npm run test 25 | - run: npm run compile 26 | - codecov/upload: 27 | file: coverage/*.json 28 | 29 | bench: 30 | <<: *defaults 31 | steps: 32 | - checkout 33 | - restore_cache: 34 | keys: 35 | - v1-dependencies-{{ checksum "package.json" }} 36 | - run: npm i 37 | - save_cache: 38 | paths: 39 | - node_modules 40 | key: v1-dependencies-{{ checksum "package.json" }} 41 | - run: npm run test 42 | - run: npm run compile 43 | - run: node build/build.js 44 | - persist_to_workspace: 45 | root: . 46 | paths: 47 | - docs 48 | - dist 49 | - README.md 50 | 51 | deploy: 52 | <<: *defaults 53 | steps: 54 | - attach_workspace: 55 | at: . 56 | - run: 57 | name: List Workspace 58 | command: ls 59 | - run: 60 | name: Authenticate with registry 61 | command: cd dist && echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc 62 | - run: 63 | name: Publish package 64 | command: cd dist && npm publish 65 | 66 | 67 | 68 | pages: 69 | <<: *defaults 70 | steps: 71 | - attach_workspace: 72 | at: . 73 | - run: 74 | name: List Workspace 75 | command: ls 76 | - add_ssh_keys: 77 | fingerprints: 78 | - 7f:7c:9d:f0:a1:14:a3:7f:c6:6b:5c:22:b2:c6:0b:3b 79 | - run: ssh-keyscan -H github.com >> ~/.ssh/known_hosts 80 | - run: git config --global user.email "ahmetcanguven44@gmail.com" 81 | - run: git config --global user.name "CirleCi" 82 | - run: git clone git@github.com:Acanguven/rapidash.git 83 | - run: cd /home/circleci/project/rapidash && git checkout gh-pages 84 | - run: cd /home/circleci/project/rapidash && rm -r ./* 85 | - run: cd /home/circleci/project/rapidash && mv ../docs/* . 86 | - run: cd /home/circleci/project/rapidash && git add . 87 | - run: cd /home/circleci/project/rapidash && git commit -m "[ci skip] Update Docs" 88 | - run: cd /home/circleci/project/rapidash && git push 89 | 90 | 91 | workflows: 92 | version: 2 93 | test-deploy: 94 | jobs: 95 | - test: 96 | filters: 97 | tags: 98 | only: /^v.*/ 99 | - bench: 100 | requires: 101 | - test 102 | filters: 103 | branches: 104 | only: master 105 | - hold: 106 | type: approval 107 | requires: 108 | - bench 109 | filters: 110 | branches: 111 | only: master 112 | - deploy: 113 | requires: 114 | - hold 115 | filters: 116 | branches: 117 | only: master 118 | - pages: 119 | requires: 120 | - deploy 121 | -------------------------------------------------------------------------------- /src/functions/equal.ts: -------------------------------------------------------------------------------- 1 | import { SolutionBuilder } from '../solution'; 2 | 3 | export type definition = (a: any, b: any) => boolean; 4 | 5 | export const solution = new SolutionBuilder('equal'); 6 | 7 | // Original repository: https://github.com/epoberezkin/fast-deep-equal 8 | // Modified & bugs fixed by @cagataycali 9 | 10 | /** 11 | * Solution Definition 12 | */ 13 | 14 | solution 15 | .description('compare two object is equal.') 16 | .example('r.equal([], []) // true') 17 | .example('r.equal({}, {}) // true') 18 | .example('r.equal(NaN, NaN) // true') 19 | .test('Check two string is equal', ['hello', 'world'], false) 20 | .test('Check two empty array is equal', [[], []], true) 21 | .test( 22 | 'Check two function is equal', 23 | [{ toString: () => 'Hello world!' }, { toString: () => 'Hello world!' }], 24 | true 25 | ) 26 | .test( 27 | 'Check two function is not equal', 28 | [{ toString: () => 'Hello world!' }, { toString: () => 'Hello rapidash!' }], 29 | false 30 | ) 31 | .test( 32 | 'Check two array of objects is equal', 33 | [ 34 | [{ a: 'a' }, { b: 'b' }], 35 | [{ a: 'a' }, { b: 'b' }], 36 | ], 37 | true 38 | ) 39 | .test( 40 | 'Check two array of objects is not equal', 41 | [ 42 | [{ a: 'a' }, { b: 'b' }], 43 | [{ a: 'a' }, { b: 'd' }], 44 | ], 45 | false 46 | ) 47 | .test( 48 | 'Check date is equal', 49 | [new Date(1996, 4, 11), new Date(1996, 4, 11)], 50 | true 51 | ) 52 | .test( 53 | 'Check date is not equal', 54 | [new Date(1996, 4, 11), new Date(2020, 1, 1)], 55 | false 56 | ) 57 | .test('Check two empty object is equal', [{}, {}], true) 58 | .test('Check two object is equal', [{ bar: {} }, {}], false) 59 | .test('Check object and string', ['', {}], false) 60 | .test('Check object and array', [[], {}], false) 61 | .test('Check two empty set is equal', [new Set(), new Set()], true) 62 | .test( 63 | 'Check two set is not equal', 64 | [new Set([3, 2, 1]), new Set([1, 2, 3])], 65 | false 66 | ) 67 | .test('Check two empty map is equal', [new Map(), new Map()], true) 68 | .test( 69 | 'Check set and map is not equal', 70 | [new Map(), new Set([3, 2, 1])], 71 | false 72 | ) 73 | .test( 74 | 'Check two filled set is not equal', 75 | [new Set([1, 2, 3]), new Set([1, 2, 4])], 76 | false 77 | ) 78 | .test( 79 | 'Check not equal arrays (different item)', 80 | [new Int32Array([1, 2, 3]), new Int32Array([1, 2, 4])], 81 | false 82 | ) 83 | .test( 84 | 'Check pseudo array and equivalent typed array are not equal', 85 | [ 86 | { '0': 1, '1': 2, length: 2, constructor: Int32Array }, 87 | new Int32Array([1, 2]), 88 | ], 89 | false 90 | ) 91 | .test( 92 | 'Check different classes but same data', 93 | [new Int32Array([1, 2, 3]), new Int16Array([1, 2, 3])], 94 | false 95 | ) 96 | .test( 97 | 'Check two empty object is equal', 98 | [{ hello: 'world' }, { hello: 'world' }], 99 | true 100 | ) 101 | .test('Check two NaN is equal', [NaN, NaN], true) 102 | .test('Check different constructors', [{}, []], false) 103 | .test( 104 | 'Check different RegExps with different sources', 105 | [/ab+c/i, /ab+cd/i], 106 | false 107 | ) 108 | .test( 109 | 'Check different RegExps with different flags', 110 | [/ab+c/i, /ab+c/g], 111 | false 112 | ) 113 | .test( 114 | 'Check different array size', 115 | [ 116 | [1, 2, 3], 117 | [1, 2], 118 | ], 119 | false 120 | ) 121 | .test( 122 | 'Check different array values', 123 | [ 124 | [1, 2, 3], 125 | [1, 2, 4], 126 | ], 127 | false 128 | ) 129 | .test( 130 | 'Check two identical objects', 131 | [ 132 | { foo: 'bar', bar: 'foo' }, 133 | { bar: 'foo', foo: 'bar' }, 134 | ], 135 | true 136 | ) 137 | .bench([ 138 | [ 139 | { foo: 'bar', bar: 'foo' }, 140 | { bar: 'foo', foo: 'bar' }, 141 | ], 142 | ]); 143 | 144 | /** 145 | * Solutions 146 | * Provide your solutions below 147 | */ 148 | 149 | solution 150 | .owner('cagataycali') 151 | .method('Recursively check object equality') 152 | .fn(function equal(a: any, b: any): boolean { 153 | if (a === b) return true; 154 | 155 | if (a && b && typeof a === 'object' && typeof b === 'object') { 156 | if (a.constructor !== b.constructor) return false; 157 | 158 | let length, i, keys; 159 | if (Array.isArray(a)) { 160 | length = a.length; 161 | if (length !== b.length) return false; 162 | for (i = length; i-- !== 0; ) { 163 | if (!equal(a[i], b[i])) return false; 164 | } 165 | return true; 166 | } 167 | 168 | if (a.constructor === RegExp) { 169 | return a.source === b.source && a.flags === b.flags; 170 | } 171 | if (a.valueOf !== Object.prototype.valueOf) { 172 | return a.valueOf() === b.valueOf(); 173 | } 174 | if ( 175 | a.__proto__ === b.__proto__ && 176 | [Set.prototype, Map.prototype].includes(a.__proto__) 177 | ) { 178 | return equal(Array.from(a), Array.from(b)); 179 | } 180 | if (a.toString !== Object.prototype.toString) { 181 | return a.toString() === b.toString(); 182 | } 183 | 184 | keys = Object.keys(a); 185 | length = keys.length; 186 | if (length !== Object.keys(b).length) return false; 187 | 188 | for (i = length; i-- !== 0; ) { 189 | /* istanbul ignore next */ 190 | if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; 191 | } 192 | 193 | for (i = length; i-- !== 0; ) { 194 | const key = keys[i]; 195 | if (!equal(a[key], b[key])) return false; 196 | } 197 | 198 | return true; 199 | } 200 | 201 | // true if both NaN, false otherwise 202 | return a !== a && b !== b; 203 | }); 204 | -------------------------------------------------------------------------------- /src/build.ts: -------------------------------------------------------------------------------- 1 | import { BenchmarkResults, Registry } from './registry'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import * as webpack from 'webpack'; 5 | import { execSync } from 'child_process'; 6 | import { distinctByProp } from './helper'; 7 | 8 | const PRE_BUILD_FOLDER = path.join(__dirname, '../pre-build'); 9 | const DIST_FOLDER = path.join(__dirname, '../dist'); 10 | const DOCS_FOLDER = path.join(__dirname, '../docs'); 11 | 12 | const SCORE_PER_SUBMIT = 10; 13 | const SCORE_RANKING_MULTIPLIER = 30; 14 | 15 | class Builder { 16 | static compiler = webpack({ 17 | entry: './pre-build/rapidash.js', 18 | output: { 19 | globalObject: 'this', 20 | libraryTarget: 'umd', 21 | library: 'rapidash', 22 | filename: 'rapidash.min.js', 23 | path: DIST_FOLDER, 24 | }, 25 | mode: 'production', 26 | }); 27 | 28 | static async benchmark() { 29 | return Registry.benchmarkWinners(); 30 | } 31 | 32 | static async build() { 33 | const benchmarkResults = await this.benchmark(); 34 | 35 | fs.mkdirSync(DIST_FOLDER, { recursive: true }); 36 | fs.mkdirSync(DOCS_FOLDER, { recursive: true }); 37 | fs.mkdirSync(PRE_BUILD_FOLDER, { recursive: true }); 38 | 39 | await Promise.all([ 40 | Builder.copyPackage(benchmarkResults), 41 | Builder.createLibraryFile(benchmarkResults), 42 | ]); 43 | 44 | Builder.createDocs(benchmarkResults); 45 | } 46 | 47 | static async createLibraryFile(benchmarkResults: BenchmarkResults) { 48 | console.info(`Creating library file`); 49 | 50 | const fileContent = benchmarkResults.reduce( 51 | (fileContent: string, result) => { 52 | const winnerFunction = result.function.solutions.find( 53 | solution => 54 | solution.owner === result.benchmarkResults.winnerSolution.owner 55 | ); 56 | fileContent += `export const ${ 57 | result.function.name 58 | }=${winnerFunction!.func.toString()};`; 59 | return fileContent; 60 | }, 61 | '' 62 | ); 63 | 64 | fs.writeFileSync( 65 | path.join(PRE_BUILD_FOLDER, './rapidash.js'), 66 | fileContent, 67 | 'utf8' 68 | ); 69 | 70 | console.info(`Compiling library file to target commonjs`); 71 | await new Promise((resolve, reject) => { 72 | Builder.compiler.run((err, stats) => { 73 | console.log(stats.toString()); 74 | 75 | if (err) return reject(err); 76 | 77 | resolve(); 78 | }); 79 | }); 80 | } 81 | 82 | static async copyPackage(benchmarkResults: BenchmarkResults) { 83 | const top3Contributors = Builder.getTopContributors(benchmarkResults).slice( 84 | 0, 85 | 3 86 | ); 87 | 88 | let winnersScript = fs.readFileSync( 89 | path.join(__dirname, '../template/npm/winners.js'), 90 | 'utf8' 91 | ); 92 | 93 | const contributorsLog = top3Contributors.reduce( 94 | (contributorsLog: string, contributor, i) => { 95 | contributorsLog += `console.log("🏆", "${i + 96 | 1}.", "https://github.com/${contributor.name}", "Solution count: ${ 97 | contributor.totalSolutions 98 | }", "Score: ${contributor.score}");`; 99 | return contributorsLog; 100 | }, 101 | '' 102 | ); 103 | 104 | winnersScript = winnersScript.replace( 105 | `{{replace_winners}}`, 106 | contributorsLog 107 | ); 108 | 109 | console.log(path.join(DIST_FOLDER, './winners.js')); 110 | fs.writeFileSync( 111 | path.join(DIST_FOLDER, './winners.js'), 112 | winnersScript, 113 | 'utf8' 114 | ); 115 | 116 | fs.copyFileSync( 117 | path.join(__dirname, '../template/npm/package.json'), 118 | path.join(DIST_FOLDER, './package.json') 119 | ); 120 | fs.copyFileSync( 121 | path.join(__dirname, '../README.md'), 122 | path.join(DIST_FOLDER, './README.md') 123 | ); 124 | } 125 | 126 | static getTopContributors(benchmarkResults: BenchmarkResults) { 127 | const contributorList: Map< 128 | string, 129 | { 130 | name: string; 131 | totalSolutions: number; 132 | score: number; 133 | } 134 | > = new Map(); 135 | 136 | benchmarkResults.forEach(result => { 137 | const benchSorted = distinctByProp( 138 | result.benchmarkResults.benchmarks, 139 | 'owner' 140 | ).sort((a, b) => b.opsSec - a.opsSec); 141 | 142 | benchSorted.forEach((bench, i) => { 143 | const existingOwner = contributorList.get(bench.owner); 144 | 145 | if (existingOwner) { 146 | existingOwner.totalSolutions++; 147 | existingOwner.score += 148 | SCORE_PER_SUBMIT + 149 | (benchSorted.length - 1 - i) * SCORE_RANKING_MULTIPLIER; 150 | contributorList.set(bench.owner, existingOwner); 151 | } else { 152 | contributorList.set(bench.owner, { 153 | name: bench.owner, 154 | totalSolutions: 1, 155 | score: 156 | SCORE_PER_SUBMIT + 157 | (benchSorted.length - 1 - i) * SCORE_RANKING_MULTIPLIER, 158 | }); 159 | } 160 | }); 161 | }); 162 | 163 | return Array.from(contributorList.values()).sort( 164 | (a, b) => b.score - a.score 165 | ); 166 | } 167 | 168 | private static createDocs(benchmarkResults: BenchmarkResults) { 169 | execSync(`mkdir -p ${DOCS_FOLDER}`); 170 | execSync( 171 | `cp -r ${path.join(__dirname, '../template/docs')}/* ${DOCS_FOLDER}` 172 | ); 173 | 174 | const introduction = fs.readFileSync( 175 | path.join(DOCS_FOLDER, './introduction.md'), 176 | 'utf8' 177 | ); 178 | 179 | const introductionReplacedContent = introduction 180 | .replace( 181 | '{{currentVersion}}', 182 | require(path.join(DIST_FOLDER, './package.json')).version 183 | ) 184 | .replace( 185 | '{{bundleSize}}', 186 | Builder.getFilesizeInBytes( 187 | path.join(DIST_FOLDER, './rapidash.min.js') 188 | ).toString() 189 | ); 190 | 191 | fs.writeFileSync( 192 | path.join(DOCS_FOLDER, './introduction.md'), 193 | introductionReplacedContent, 194 | 'utf8' 195 | ); 196 | 197 | const functionTemplate = fs.readFileSync( 198 | path.join(__dirname, '../template/docs/function-template.md'), 199 | 'utf8' 200 | ); 201 | const functionsContent = fs.readFileSync( 202 | path.join(__dirname, '../template/docs/functions.md'), 203 | 'utf8' 204 | ); 205 | 206 | const functionList = benchmarkResults.reduce((markdownContent, fn) => { 207 | const solutionDetails: Record = { 208 | '{{solutionOwner}}': fn.benchmarkResults.winnerSolution.owner, 209 | '{{functionDescription}}': fn.function.descr, 210 | '{{functionExample}}': fn.function.examples 211 | .map(example => { 212 | return '```js\r\n' + example + '\r\n```\r\n'; 213 | }) 214 | .join(' '), 215 | '{{functionName}}': fn.function.name, 216 | '{{hz}}': Math.round( 217 | fn.benchmarkResults.winnerSolution.hz 218 | ).toLocaleString(), 219 | }; 220 | 221 | let functionMarkdownContent = functionTemplate; 222 | 223 | Object.keys(solutionDetails).forEach(key => { 224 | functionMarkdownContent = functionMarkdownContent.replace( 225 | new RegExp(key, 'g'), 226 | solutionDetails[key] 227 | ); 228 | }); 229 | 230 | return markdownContent + functionMarkdownContent; 231 | }, ''); 232 | 233 | fs.writeFileSync( 234 | path.join(DOCS_FOLDER, './functions.md'), 235 | functionsContent.replace('{{functionsContent}}', functionList), 236 | 'utf8' 237 | ); 238 | 239 | const sideBarItem = ` * [{{functionName}}](functions.md#{{functionName}})`; 240 | 241 | const sideBarContent = fs.readFileSync( 242 | path.join(DOCS_FOLDER, './_sidebar.md'), 243 | 'utf8' 244 | ); 245 | 246 | fs.writeFileSync( 247 | path.join(DOCS_FOLDER, './_sidebar.md'), 248 | sideBarContent.replace( 249 | '{{sideBarItems}}', 250 | benchmarkResults 251 | .map(fn => 252 | sideBarItem.replace( 253 | new RegExp('{{functionName}}', 'g'), 254 | fn.function.name 255 | ) 256 | ) 257 | .join('\r\n') 258 | ) 259 | ); 260 | 261 | const rankingContent = fs.readFileSync( 262 | path.join(DOCS_FOLDER, './rankings.md'), 263 | 'utf8' 264 | ); 265 | 266 | const topContributors = Builder.getTopContributors(benchmarkResults) 267 | .map( 268 | (solutionOwner, i) => 269 | `| ${i + 1} | ![${solutionOwner.name}](https://github.com/${ 270 | solutionOwner.name 271 | }.png?size=60) | [${solutionOwner.name}](https://github.com/${ 272 | solutionOwner.name 273 | }) | ${solutionOwner.totalSolutions} | ${ 274 | solutionOwner.score 275 | } |\r\n` 276 | ) 277 | .join(''); 278 | 279 | fs.writeFileSync( 280 | path.join(DOCS_FOLDER, './rankings.md'), 281 | rankingContent.replace(`{{rankings}}`, topContributors), 282 | 'utf8' 283 | ); 284 | } 285 | 286 | private static getFilesizeInBytes(path: string) { 287 | const stats = fs.statSync(path); 288 | return stats.size / 1000; 289 | } 290 | } 291 | 292 | Builder.build(); 293 | --------------------------------------------------------------------------------