├── .browserslistrc ├── .commitlintrc.json ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── .huskyrc.json ├── .lintstagedrc ├── .prettierignore ├── .prettierrc ├── .storybook ├── main.js ├── manager.js ├── preview.js └── storybook.css ├── .stylelintrc ├── .travis.yml ├── .versionrc.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── docs ├── WELCOME.md └── docs.demo.js ├── jest.config.js ├── package.json ├── rollup.config.js ├── src ├── algorithms │ ├── search │ │ ├── array │ │ │ ├── additional-demos │ │ │ │ ├── search-performance-comparison-test.demo.css │ │ │ │ └── search-performance-comparison-test.demo.js │ │ │ ├── binary-search │ │ │ │ ├── demo │ │ │ │ │ └── binary-search.demo.js │ │ │ │ └── src │ │ │ │ │ ├── binary-search.js │ │ │ │ │ └── binary-search.test.js │ │ │ ├── demo-utils │ │ │ │ ├── search-performance-test.demo.css │ │ │ │ └── search-performance-test.demo.js │ │ │ └── linear-search │ │ │ │ ├── demo │ │ │ │ └── linear-search.demo.js │ │ │ │ └── src │ │ │ │ ├── linear-search.js │ │ │ │ └── linear-search.test.js │ │ └── string │ │ │ ├── additional-demos │ │ │ ├── search-performance-comparison-test.demo.css │ │ │ └── search-performance-comparison-test.demo.js │ │ │ ├── boyer-moore-horspool-search │ │ │ ├── demo │ │ │ │ └── boyer-moore-horspool-search.demo.js │ │ │ └── src │ │ │ │ ├── boyer-moore-horspool-search.js │ │ │ │ └── boyer-moore-horspool-search.test.js │ │ │ ├── demo-utils │ │ │ ├── declaration-of-independence.js │ │ │ ├── search-performance-test.demo.css │ │ │ └── search-performance-test.demo.js │ │ │ └── naive-search │ │ │ ├── demo │ │ │ └── naive-search.demo.js │ │ │ └── src │ │ │ ├── naive-search.js │ │ │ └── naive-search.test.js │ ├── set │ │ ├── additional-demos │ │ │ ├── view-all.demo.css │ │ │ └── view-all.demo.js │ │ ├── intersection │ │ │ ├── demo │ │ │ │ ├── intersection.demo.css │ │ │ │ └── intersection.demo.js │ │ │ └── src │ │ │ │ ├── intersection.js │ │ │ │ └── intersection.test.js │ │ ├── set-difference │ │ │ ├── demo │ │ │ │ ├── set-difference.demo.css │ │ │ │ └── set-difference.demo.js │ │ │ └── src │ │ │ │ ├── set-difference.js │ │ │ │ └── set-difference.test.js │ │ ├── symmetric-difference │ │ │ ├── demo │ │ │ │ ├── symmetric-difference.demo.css │ │ │ │ └── symmetric-difference.demo.js │ │ │ └── src │ │ │ │ ├── symmetric-difference.js │ │ │ │ └── symmetric-difference.test.js │ │ └── union │ │ │ ├── demo │ │ │ ├── union.demo.css │ │ │ └── union.demo.js │ │ │ └── src │ │ │ ├── union.js │ │ │ └── union.test.js │ └── sort │ │ ├── additional-demos │ │ ├── sort-performance-comparison-test.demo.css │ │ └── sort-performance-comparison-test.demo.js │ │ ├── bubble-sort │ │ ├── demo │ │ │ └── bubble-sort.demo.js │ │ └── src │ │ │ ├── bubble-sort.js │ │ │ └── bubble-sort.test.js │ │ ├── counting-sort │ │ ├── demo │ │ │ └── counting-sort.demo.js │ │ └── src │ │ │ ├── counting-sort.js │ │ │ └── counting-sort.test.js │ │ ├── demo-utils │ │ ├── sort-performance-test.demo.css │ │ └── sort-performance-test.demo.js │ │ ├── insertion-sort │ │ ├── demo │ │ │ └── insertion-sort.demo.js │ │ └── src │ │ │ ├── insertion-sort.js │ │ │ └── insertion-sort.test.js │ │ ├── merge-sort │ │ ├── demo │ │ │ └── merge-sort.demo.js │ │ └── src │ │ │ ├── merge-sort.js │ │ │ └── merge-sort.test.js │ │ ├── quick-sort │ │ ├── demo │ │ │ └── quick-sort.demo.js │ │ └── src │ │ │ ├── quick-sort.js │ │ │ └── quick-sort.test.js │ │ └── selection-sort │ │ ├── demo │ │ └── selection-sort.demo.js │ │ └── src │ │ ├── selection-sort.js │ │ └── selection-sort.test.js ├── data-structures │ ├── array │ │ ├── demo │ │ │ └── array.demo.js │ │ └── src │ │ │ └── array.js │ ├── binary-search-tree │ │ ├── demo │ │ │ ├── binary-search-tree-with-display.js │ │ │ ├── binary-search-tree.demo.css │ │ │ └── binary-search-tree.demo.js │ │ └── src │ │ │ ├── binary-search-tree.js │ │ │ ├── binary-search-tree.test.js │ │ │ ├── node.js │ │ │ └── node.test.js │ ├── directed-graph │ │ └── src │ │ │ ├── directed-graph.js │ │ │ └── directed-graph.test.js │ ├── doubly-linked-list │ │ ├── demo │ │ │ ├── doubly-linked-list.demo.css │ │ │ ├── doubly-linked-list.demo.js │ │ │ ├── node.demo.css │ │ │ └── node.demo.js │ │ └── src │ │ │ ├── doubly-linked-list.js │ │ │ ├── doubly-linked-list.test.js │ │ │ ├── node.js │ │ │ └── node.test.js │ ├── hash-table │ │ ├── demo │ │ │ └── hash-table.demo.js │ │ └── src │ │ │ └── hash-table.js │ ├── linked-list │ │ ├── demo │ │ │ ├── linked-list.demo.css │ │ │ ├── linked-list.demo.js │ │ │ ├── node.demo.css │ │ │ └── node.demo.js │ │ └── src │ │ │ ├── linked-list.js │ │ │ ├── linked-list.test.js │ │ │ ├── node.js │ │ │ └── node.test.js │ ├── priority-queue │ │ ├── demo │ │ │ ├── priority-queue.demo.css │ │ │ └── priority-queue.demo.js │ │ └── src │ │ │ ├── priority-queue.js │ │ │ └── priority-queue.test.js │ ├── queue │ │ ├── demo │ │ │ ├── queue-performance-test.demo.css │ │ │ ├── queue-performance-test.demo.js │ │ │ ├── queue.demo.css │ │ │ └── queue.demo.js │ │ └── src │ │ │ ├── queue-from-doubly-linked-list.js │ │ │ ├── queue-from-doubly-linked-list.test.js │ │ │ ├── queue.js │ │ │ └── queue.test.js │ ├── set │ │ ├── demo │ │ │ ├── set.demo.css │ │ │ └── set.demo.js │ │ └── src │ │ │ ├── set.js │ │ │ └── set.test.js │ └── stack │ │ ├── demo │ │ ├── stack-performance-test.demo.css │ │ ├── stack-performance-test.demo.js │ │ ├── stack.demo.css │ │ └── stack.demo.js │ │ └── src │ │ ├── stack-from-linked-list.js │ │ ├── stack-from-linked-list.test.js │ │ ├── stack.js │ │ └── stack.test.js └── main.js ├── storybook-dist ├── 0.733027ac.iframe.bundle.js ├── 10.908e40c81ef5762e4e6a.manager.bundle.js ├── 10.908e40c81ef5762e4e6a.manager.bundle.js.LICENSE.txt ├── 11.404f928febacec26f4c1.manager.bundle.js ├── 12.6e3bba1ebcbceadd7f90.manager.bundle.js ├── 13.f4916ecfc1ae8db3695f.manager.bundle.js ├── 13.f4916ecfc1ae8db3695f.manager.bundle.js.LICENSE.txt ├── 7.332c6bddae98466fc74f.manager.bundle.js ├── 8.31b1fcf1df0801c904ff.manager.bundle.js ├── 8.31b1fcf1df0801c904ff.manager.bundle.js.LICENSE.txt ├── 9.80b1068865ea380144c5.manager.bundle.js ├── favicon.ico ├── iframe.html ├── index.html ├── main.cb4f8469064561bafcc7.manager.bundle.js ├── main.d5a969db.iframe.bundle.js ├── project.json ├── runtime~main.5b3b37a5880e4df84c98.manager.bundle.js ├── runtime~main.a51f8bae.iframe.bundle.js ├── vendors~main.638b5667.iframe.bundle.js ├── vendors~main.638b5667.iframe.bundle.js.LICENSE.txt ├── vendors~main.638b5667.iframe.bundle.js.map ├── vendors~main.db18f16359f60198e93f.manager.bundle.js └── vendors~main.db18f16359f60198e93f.manager.bundle.js.LICENSE.txt └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | [production] 2 | >0.2% 3 | not dead 4 | not op_mini all 5 | 6 | [development] 7 | last 1 chrome version 8 | last 1 firefox version 9 | last 1 safari version 10 | -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@commitlint/config-conventional" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | .idea 3 | /build 4 | /coverage 5 | /dist 6 | /node_modules 7 | /storybook-dist 8 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:import/errors", 10 | "plugin:import/warnings", 11 | "plugin:jest/recommended", 12 | "plugin:prettier/recommended", 13 | "plugin:react/recommended" 14 | ], 15 | "overrides": [ 16 | { 17 | "files": "**/*.test.js", 18 | "env": { 19 | "jest": true 20 | } 21 | } 22 | ], 23 | "parser": "@babel/eslint-parser", 24 | "parserOptions": { 25 | "requireConfigFile": false, 26 | "babelOptions": { 27 | "presets": ["@babel/preset-react"] 28 | } 29 | }, 30 | "root": true, 31 | "rules": { 32 | "no-case-declarations": "off", 33 | "react/prop-types": "off" 34 | }, 35 | "settings": { 36 | "react": { 37 | "version": "17.0.2" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Title: 2 | 3 | ## Description: 4 | 5 | 6 | ## Issues this closes or references (if any): 7 | 8 | 9 | ## Test plan: 10 | 11 | 12 | ## QA risk (high, medium, low): 13 | 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /dist 14 | 15 | # misc 16 | .DS_Store 17 | .idea 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | .npmrc 28 | -------------------------------------------------------------------------------- /.huskyrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged && yarn test:coverage", 4 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.css": [ 3 | "stylelint --fix" 4 | ], 5 | "*.{js,jsx,ts,tsx}": [ 6 | "eslint --fix", 7 | "prettier --write" 8 | ], 9 | "*.md": [ 10 | "prettier --write" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | .idea 3 | /build 4 | /coverage 5 | /dist 6 | /node_modules 7 | /storybook-dist 8 | package.json 9 | CHANGELOG.md 10 | *.css 11 | .github/ 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "printWidth": 80, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "es5", 8 | "quoteProps": "as-needed", 9 | "jsxSingleQuote": false, 10 | "bracketSpacing": true, 11 | "arrowParens": "avoid", 12 | "endOfLine": "auto", 13 | "proseWrap": "preserve", 14 | "htmlWhitespaceSensitivity": "css" 15 | } 16 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['../docs/**/*.demo.js', '../src/**/*.demo.js'], 3 | // babel: async options => ({ 4 | // ...options, 5 | // plugins: [ 6 | // ...options.plugins, 7 | // ['@babel/plugin-proposal-private-property-in-object', { loose: true }], 8 | // ], 9 | // }), 10 | } 11 | -------------------------------------------------------------------------------- /.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/addons' 2 | 3 | addons.setConfig({ 4 | sidebar: { 5 | showRoots: true, 6 | }, 7 | }) 8 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import './storybook.css' 2 | -------------------------------------------------------------------------------- /.storybook/storybook.css: -------------------------------------------------------------------------------- 1 | /* GENERAL */ 2 | html { 3 | color: #071a32; 4 | font-family: "Helvetica Neue", Arial, sans-serif; 5 | line-height: 1.5; 6 | } 7 | 8 | html, 9 | body { 10 | box-sizing: border-box; 11 | padding: 0; 12 | margin: 0; 13 | font-size: 16px; 14 | } 15 | 16 | body { 17 | padding: 2rem; 18 | } 19 | 20 | * { 21 | box-sizing: border-box; 22 | } 23 | 24 | /* BUTTON */ 25 | .button { 26 | padding: 0.5rem 0.75rem; 27 | font-size: 1rem; 28 | font-family: "Helvetica Neue", Arial, sans-serif; 29 | border-radius: 0.5rem; 30 | border: none; 31 | background: #216869; 32 | color: #fff; 33 | cursor: pointer; 34 | position: relative; 35 | max-width: 18rem; 36 | word-break: break-word; 37 | word-wrap: break-word; 38 | transition: all 0.5s; 39 | } 40 | 41 | .button:focus, 42 | .button:hover { 43 | outline: none; 44 | } 45 | 46 | .button.outline:focus::before, 47 | .button.outline:hover::before { 48 | box-shadow: #216869 0 0 0 0.0625rem; 49 | content: ""; 50 | height: calc(100% + 0.25rem); 51 | left: 0; 52 | margin-left: -0.25rem; 53 | margin-top: -0.25rem; 54 | pointer-events: none; 55 | position: absolute; 56 | top: 0; 57 | width: calc(100% + 0.25rem); 58 | border-radius: inherit; 59 | padding: 0.125rem; 60 | } 61 | 62 | .button.underline { 63 | border: none; 64 | border-radius: 0; 65 | background: transparent; 66 | color: #216869; 67 | padding: 0; 68 | margin: 0.25rem 0.5rem; 69 | font-weight: bold; 70 | } 71 | 72 | .button.underline:focus::before, 73 | .button.underline:hover::before { 74 | border-bottom: 0.0625rem solid currentColor; 75 | bottom: -0.1875rem; 76 | content: ""; 77 | height: 0; 78 | left: 0; 79 | position: absolute; 80 | right: 0; 81 | pointer-events: none; 82 | } 83 | 84 | /* TEXT INPUT */ 85 | .ti { 86 | padding: 0.5rem 0.75rem; 87 | font-size: 1rem; 88 | font-family: "Helvetica Neue", Arial, sans-serif; 89 | border: 0.0625rem solid #071a32; 90 | border-radius: 0.25rem; 91 | background: #fff; 92 | color: #071a32; 93 | position: relative; 94 | } 95 | 96 | .ti:focus, 97 | .ti:hover { 98 | outline: none; 99 | border-bottom: 0.25rem solid #071a32; 100 | padding: 0.5rem 0.75rem 0.3125rem 0.75rem; 101 | } 102 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "rules": { 4 | "declaration-block-no-redundant-longhand-properties": null, 5 | "selector-class-pattern": null 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | cache: 5 | directories: 6 | - node_modules 7 | install: 8 | - yarn install 9 | - yarn global add codecov 10 | script: 11 | - yarn lint 12 | - yarn test:coverage 13 | - codecov 14 | on: 15 | branch: master 16 | -------------------------------------------------------------------------------- /.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "prerelease": "yarn lint && yarn test:coverage && yarn build" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at thawkin3@byu.net. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Introduction 4 | 5 | Welcome! Thanks for your interest in contributing to this project! 6 | 7 | ## Purpose 8 | 9 | The purpose of this project is to help others learn and understand data 10 | structures and algorithms from a JavaScript standpoint. Rather than 11 | containing only snippets of code with accompanying explanations, this 12 | project is meant to provide an eager learner with fully working code, 13 | good test cases, and a playground full of examples. 14 | 15 | ## Content 16 | 17 | Each data structure or algorithm should meet the following requirements: 18 | 19 | - Be implemented in JavaScript 20 | - Not depend on any third-party dependencies 21 | - Have 100% test coverage 22 | - Have Storybook examples to help people visualize the concept 23 | 24 | ## Bug Reports 25 | 26 | If you would like to file a bug, please create an 27 | [issue on the repo in GitHub](https://github.com/thawkin3/js-data-structures-and-algorithms/issues). 28 | You should click the "New issue" button and then select the 29 | "Bug report" template. Please be as detailed as you can and 30 | include any additional information that you feel is relevant. 31 | 32 | ## Feature Requests 33 | 34 | If you would like to request a feature such as a new data structure, 35 | new algorithm, new or improved example, or any other improvements, 36 | please create an 37 | [issue on the repo in GitHub](https://github.com/thawkin3/js-data-structures-and-algorithms/issues). 38 | You should click the "New issue" button and then select the 39 | "Feature request" template. I will reply as soon as possible 40 | so we can take steps toward implementing your suggestion. 41 | Or, better yet, after filing an issue, create a pull 42 | request to implement the feature yourself! 43 | 44 | ## Pull Requests 45 | 46 | If you would like to contribute to this project, please submit an 47 | issue, write some code, and then create a pull request. 48 | 49 | This project uses the following tools for automation and linting during the development process: 50 | 51 | - [prettier](https://prettier.io/) for code formatting (JS and MD files) 52 | - [stylelint](https://stylelint.io/) for CSS formatting 53 | = [eslint](https://eslint.org/) for checking syntax errors 54 | - [commitizen](https://github.com/commitizen/cz-cli) for commit message formatting 55 | - [lint-staged](https://www.npmjs.com/package/lint-staged) to lint changes before committing them 56 | - [validate-commit-msg](https://www.npmjs.com/package/validate-commit-msg) 57 | to validate the commit message is in a standard format 58 | - [Husky](https://github.com/typicode/husky) for git hooks to run some validation 59 | - [standard-version](https://github.com/conventional-changelog/standard-version) 60 | to automate versioning and CHANGELOG generation 61 | 62 | The process of adding, committing, and pushing your code will look like this: 63 | 64 | - Write your code 65 | - `git add .` 66 | - `yarn cz` (Note that this replaces `git commit`) 67 | - (Husky then runs the git hooks to verify that the tests are passing, that 68 | the code has no errors, and that the commit message is in the proper format) 69 | - `git push` 70 | 71 | ## Continuous Integration 72 | 73 | This project has continuous integration set up through [Travis CI](https://travis-ci.com/) 74 | 75 | ## Cutting a New Release 76 | 77 | While the primary purpose of this project is education, the data structures 78 | and algorithms are implemented here as real working code and can be used as 79 | such. This project is published on npm as [js-data-structures-and-algorithms](https://www.npmjs.com/package/js-data-structures-and-algorithms). 80 | 81 | When we are ready to cut a new release, we follow these steps: 82 | 83 | - `yarn release` (This uses [standard-version](https://github.com/conventional-changelog/standard-version) 84 | to automate bumping the version and generating the CHANGELOG) 85 | - `git push --follow-tags origin master` (pushes the new commit and the new tag to GitHub) 86 | - `yarn publish` (publishes the new package version on npm) 87 | 88 | Note that only those with access can publish a new package version. 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tyler Hawkins 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 | 23 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | // For Jest, which requires ES6 modules to be transpiled 4 | test: { 5 | presets: [ 6 | [ 7 | '@babel/preset-env', 8 | { 9 | targets: { 10 | node: 'current', 11 | }, 12 | }, 13 | ], 14 | ], 15 | }, 16 | // For Rollup, which requires ES6 modules to be left alone by Babel 17 | production: { 18 | presets: [['@babel/preset-env', { modules: false }]], 19 | }, 20 | // For Storybook 21 | storybook: { 22 | presets: [ 23 | [ 24 | '@babel/preset-env', 25 | { 26 | targets: { 27 | node: 'current', 28 | }, 29 | }, 30 | ], 31 | ], 32 | plugins: [ 33 | ['@babel/plugin-proposal-private-property-in-object', { loose: true }], 34 | ], 35 | }, 36 | }, 37 | } 38 | -------------------------------------------------------------------------------- /docs/WELCOME.md: -------------------------------------------------------------------------------- 1 | # JS Data Structures and Algorithms 2 | 3 | Welcome! This Storybook static site serves as a playground for exploring various data structures and algorithms. 4 | 5 | Each example is interactive. 6 | 7 | For a more in-depth look, check out the [source code on GitHub](https://github.com/thawkin3/js-data-structures-and-algorithms). 8 | -------------------------------------------------------------------------------- /docs/docs.demo.js: -------------------------------------------------------------------------------- 1 | import { doc } from 'storybook-readme' 2 | import WELCOME from './WELCOME.md' 3 | 4 | export default { 5 | title: 'Introduction/Docs', 6 | } 7 | 8 | export const Welcome = doc(WELCOME) 9 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | collectCoverage: false, 4 | collectCoverageFrom: [ 5 | 'src/**/src/*.{js,ts,tsx}', 6 | '!src/**/src/{array,hash-table}.{js,ts,tsx}', 7 | ], 8 | coverageDirectory: 'coverage', 9 | coverageThreshold: { 10 | global: { 11 | branches: 95, 12 | functions: 95, 13 | lines: 95, 14 | statements: 95, 15 | }, 16 | }, 17 | testEnvironment: 'node', 18 | testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-data-structures-and-algorithms", 3 | "version": "1.13.0", 4 | "description": "Data structures and algorithms implemented in JavaScript", 5 | "main": "dist/main.cjs.min.js", 6 | "module": "dist/main.esm.min.js", 7 | "browser": "dist/main.umd.min.js", 8 | "files": [ 9 | "dist" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/thawkin3/js-data-structures-and-algorithms.git" 14 | }, 15 | "keywords": [ 16 | "algorithm", 17 | "algorithm-concepts", 18 | "algorithms", 19 | "coding-interviews", 20 | "computer-science", 21 | "data-structure", 22 | "data-structure-concepts", 23 | "data-structures", 24 | "data-structures-algorithms", 25 | "interview", 26 | "interview-preparation", 27 | "javascript", 28 | "javascript-algorithms", 29 | "whiteboard" 30 | ], 31 | "author": "Tyler Hawkins (http://tylerhawkins.info)", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/thawkin3/js-data-structures-and-algorithms/issues" 35 | }, 36 | "homepage": "http://tylerhawkins.info/js-data-structures-and-algorithms/storybook-dist", 37 | "scripts": { 38 | "build": "NODE_ENV=production rollup --config", 39 | "build-storybook": "NODE_ENV=storybook build-storybook -c .storybook -o storybook-dist", 40 | "cz": "git-cz", 41 | "eslint": "eslint . --ext .js,.jsx,.ts,.tsx", 42 | "lint": "run-p eslint prettier stylelint", 43 | "prettier": "prettier --write \"**/*.{js,jsx,ts,tsx,md}\"", 44 | "prettier-watch": "onchange \"**/*.{js,jsx,ts,tsx,md}\" -- prettier --write {{changed}}", 45 | "release": "standard-version", 46 | "storybook": "NODE_ENV=storybook start-storybook -p 9009", 47 | "stylelint": "stylelint --fix \"src/**/*.css\"", 48 | "test": "NODE_ENV=test jest", 49 | "test:coverage": "NODE_ENV=test jest --coverage", 50 | "test:logs": "SHOW_LOGS=1 NODE_ENV=test jest --watch", 51 | "test:watch": "NODE_ENV=test jest --watch" 52 | }, 53 | "dependencies": {}, 54 | "devDependencies": { 55 | "@babel/core": "^7.19.6", 56 | "@babel/eslint-parser": "^7.19.1", 57 | "@babel/preset-env": "^7.19.4", 58 | "@babel/preset-react": "^7.18.6", 59 | "@commitlint/cli": "^12.1.4", 60 | "@commitlint/config-conventional": "^12.1.4", 61 | "@rollup/plugin-babel": "^5.3.0", 62 | "@rollup/plugin-commonjs": "^19.0.0", 63 | "@rollup/plugin-node-resolve": "^13.0.0", 64 | "@storybook/addons": "^6.5.13", 65 | "@storybook/react": "^6.5.13", 66 | "babel-jest": "^29.2.2", 67 | "babel-loader": "^9.0.0", 68 | "commitizen": "^4.2.5", 69 | "cz-conventional-changelog": "^3.3.0", 70 | "eslint": "^8.26.0", 71 | "eslint-config-prettier": "^8.5.0", 72 | "eslint-plugin-import": "^2.26.0", 73 | "eslint-plugin-jest": "^27.1.3", 74 | "eslint-plugin-prettier": "^4.2.1", 75 | "eslint-plugin-react": "^7.31.10", 76 | "husky": "^4.3.8", 77 | "jest": "^29.2.2", 78 | "lint-staged": "^11.0.0", 79 | "npm-run-all": "^4.1.5", 80 | "onchange": "^7.1.0", 81 | "prettier": "^2.7.1", 82 | "react": "^17.0.2", 83 | "react-dom": "^17.0.2", 84 | "react-tree-graph": "6.0.0", 85 | "rollup": "^2.51.2", 86 | "rollup-plugin-delete": "^2.0.0", 87 | "rollup-plugin-terser": "^7.0.2", 88 | "standard-version": "^9.5.0", 89 | "storybook-readme": "^5.0.9", 90 | "stylelint": "^14.14.0", 91 | "stylelint-config-standard": "^29.0.0" 92 | }, 93 | "config": { 94 | "commitizen": { 95 | "path": "cz-conventional-changelog" 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import { nodeResolve } from '@rollup/plugin-node-resolve' 4 | import { terser } from 'rollup-plugin-terser' 5 | import del from 'rollup-plugin-delete' 6 | import pkg from './package.json' 7 | 8 | export default [ 9 | // browser-friendly UMD build 10 | { 11 | input: 'src/main.js', 12 | output: [ 13 | // un-minified 14 | { 15 | file: 'dist/main.umd.js', 16 | format: 'umd', 17 | name: 'jsDsa', 18 | }, 19 | // minified 20 | { 21 | file: pkg.browser, 22 | format: 'umd', 23 | name: 'jsDsa', 24 | plugins: [terser()], 25 | sourcemap: true, 26 | }, 27 | ], 28 | plugins: [ 29 | del({ targets: 'dist/*' }), 30 | nodeResolve(), 31 | commonjs(), 32 | babel({ 33 | babelHelpers: 'bundled', 34 | exclude: ['node_modules/**'], 35 | }), 36 | ], 37 | }, 38 | 39 | // CommonJS (for Node) and ES module (for bundlers) build. 40 | { 41 | input: 'src/main.js', 42 | output: [ 43 | // CommonJS (un-minified and minified) 44 | { file: 'dist/main.cjs.js', format: 'cjs', name: 'jsDsa' }, 45 | { 46 | file: pkg.main, 47 | format: 'cjs', 48 | name: 'jsDsa', 49 | plugins: [terser()], 50 | sourcemap: true, 51 | }, 52 | // ES module (un-minified and minified) 53 | { file: 'dist/main.esm.js', format: 'es', name: 'jsDsa' }, 54 | { 55 | file: pkg.module, 56 | format: 'es', 57 | name: 'jsDsa', 58 | plugins: [terser()], 59 | sourcemap: true, 60 | }, 61 | ], 62 | plugins: [ 63 | babel({ 64 | babelHelpers: 'bundled', 65 | exclude: ['node_modules/**'], 66 | }), 67 | ], 68 | }, 69 | ] 70 | -------------------------------------------------------------------------------- /src/algorithms/search/array/additional-demos/search-performance-comparison-test.demo.css: -------------------------------------------------------------------------------- 1 | .searchComparisonDemo button, 2 | .searchComparisonDemo label { 3 | margin-bottom: 1rem; 4 | display: inline-block; 5 | } 6 | 7 | .searchComparisonDemo label span { 8 | margin-right: 1rem; 9 | } 10 | 11 | .searchComparisonDemo h3 { 12 | display: inline-block; 13 | margin: 0.5rem 0; 14 | } 15 | 16 | .searchComparisonDemo .haystackTextAreaLabel { 17 | width: 100%; 18 | } 19 | 20 | .searchComparisonDemo .haystackTextAreaLabel span { 21 | margin-bottom: 0.5rem; 22 | display: inline-block; 23 | } 24 | 25 | .searchComparisonDemo .haystackTextArea { 26 | width: 100%; 27 | height: 10rem; 28 | resize: none; 29 | } 30 | 31 | .searchComparisonDemo .arrayHaystack { 32 | margin: 1rem 0; 33 | } 34 | 35 | .searchComparisonDemo .arrayHaystack .arrayHaystackCharacters { 36 | white-space: nowrap; 37 | overflow-x: scroll; 38 | border: 1px solid #071a32; 39 | padding: 0.5rem; 40 | background: #f6f6f6; 41 | border-radius: 0.25rem; 42 | margin-top: 0.55rem; 43 | } 44 | -------------------------------------------------------------------------------- /src/algorithms/search/array/binary-search/demo/binary-search.demo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SearchPerformanceTest } from '../../demo-utils/search-performance-test.demo' 3 | import { binarySearch } from '../src/binary-search' 4 | 5 | export default { 6 | title: 'Algorithms/Search (Array)/Binary Search', 7 | } 8 | 9 | export const binarySearchPerformanceTest = () => ( 10 | 14 | ) 15 | -------------------------------------------------------------------------------- /src/algorithms/search/array/binary-search/src/binary-search.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Binary Search 3 | * 4 | * Starts at the midpoint and continues to cut the array into halves 5 | * Only can be used for sorted arrays 6 | * 7 | * Best case performance: Ω(1) (the element you're looking for is located at the array's midpoint) 8 | * Average case performance: 0(log n) 9 | * Worst case performance: O(log n) (the element is located near the beginning or end of the array) 10 | */ 11 | 12 | export const binarySearch = (haystack, needle, showLogs) => { 13 | if ( 14 | !(haystack instanceof Array) || 15 | typeof needle === 'undefined' || 16 | needle === null 17 | ) { 18 | return -1 19 | } 20 | 21 | let searchableHaystack = [...haystack] 22 | let i = 1 23 | let fullHaystackMidpointIndex = 0 24 | 25 | while (searchableHaystack.length > 0) { 26 | const midpointIndex = Math.floor(searchableHaystack.length / 2) 27 | fullHaystackMidpointIndex += midpointIndex 28 | 29 | /* istanbul ignore next */ 30 | showLogs && 31 | console.log( 32 | `iteration ${i}: midpoint index: ${fullHaystackMidpointIndex}; array to search: ${searchableHaystack.join( 33 | ', ' 34 | )}; ${needle} === ${searchableHaystack[midpointIndex]} ? ... ${ 35 | needle === searchableHaystack[midpointIndex] 36 | }!` 37 | ) 38 | 39 | if (searchableHaystack[midpointIndex] === needle) { 40 | return fullHaystackMidpointIndex 41 | } else if (searchableHaystack[midpointIndex] > needle) { 42 | fullHaystackMidpointIndex -= midpointIndex 43 | searchableHaystack = searchableHaystack.slice(0, midpointIndex) 44 | } else { 45 | fullHaystackMidpointIndex += 1 46 | searchableHaystack = searchableHaystack.slice(midpointIndex + 1) 47 | } 48 | 49 | i++ 50 | } 51 | return -1 52 | } 53 | -------------------------------------------------------------------------------- /src/algorithms/search/array/binary-search/src/binary-search.test.js: -------------------------------------------------------------------------------- 1 | import { binarySearch } from './binary-search' 2 | 3 | const showLogs = process.env.SHOW_LOGS === '1' 4 | 5 | describe('binarySearch', () => { 6 | it('returns the index when it finds an element that exists in the array', () => { 7 | const testArray = [1, 3, 5, 8, 9, 10, 12] 8 | const testArray2 = [ 9 | 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 10 | ] 11 | 12 | expect(binarySearch(testArray, 10, showLogs)).toBe(5) 13 | expect(binarySearch(testArray, 1, showLogs)).toBe(0) 14 | expect(binarySearch(testArray2, 14, showLogs)).toBe(7) 15 | expect(binarySearch(testArray2, 0, showLogs)).toBe(0) 16 | expect(binarySearch(testArray2, 22, showLogs)).toBe(11) 17 | }) 18 | 19 | it('can handle sorted arrays with duplicate elements', () => { 20 | const testArray = [1, 1, 3, 3, 3] 21 | const testArray2 = [1, 1, 3, 3] 22 | 23 | expect(binarySearch(testArray, 3, showLogs)).toBe(2) 24 | expect(binarySearch(testArray2, 3, showLogs)).toBe(2) 25 | }) 26 | 27 | it('returns -1 when it does not find the element it is looking for', () => { 28 | const testArray = [1, 3, 5, 8, 9, 10, 12] 29 | const testArray2 = [ 30 | 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 31 | ] 32 | 33 | expect(binarySearch(testArray, 999, showLogs)).toBe(-1) 34 | expect(binarySearch(testArray2, 999, showLogs)).toBe(-1) 35 | }) 36 | 37 | it('handles a missing needle argument', () => { 38 | const testArray = [1, 3, 5, 8, 9, 10, 12] 39 | expect(binarySearch(testArray, undefined, showLogs)).toBe(-1) 40 | expect(binarySearch(testArray, null, showLogs)).toBe(-1) 41 | }) 42 | 43 | it('handles a non-array haystack argument', () => { 44 | expect(binarySearch('a string', 42, showLogs)).toBe(-1) 45 | expect(binarySearch(100, 42, showLogs)).toBe(-1) 46 | expect(binarySearch(null, 42, showLogs)).toBe(-1) 47 | expect(binarySearch(undefined, 42, showLogs)).toBe(-1) 48 | expect(binarySearch({ someKey: 'someValue' }, 42, showLogs)).toBe(-1) 49 | }) 50 | 51 | it('can search for a string in an array of strings', () => { 52 | const testArray = ['apple', 'banana', 'cat', 'dog', 'egg', 'fan', 'giraffe'] 53 | expect(binarySearch(testArray, 'cat', showLogs)).toBe(2) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /src/algorithms/search/array/demo-utils/search-performance-test.demo.css: -------------------------------------------------------------------------------- 1 | .searchDemo button, 2 | .searchDemo label { 3 | margin-bottom: 1rem; 4 | display: inline-block; 5 | } 6 | 7 | .searchDemo label span { 8 | margin-right: 1rem; 9 | } 10 | 11 | .searchDemo h3 { 12 | display: inline-block; 13 | margin: 0.5rem 0; 14 | } 15 | 16 | .searchDemo .haystackTextAreaLabel { 17 | width: 100%; 18 | } 19 | 20 | .searchDemo .haystackTextAreaLabel span { 21 | margin-bottom: 0.5rem; 22 | display: inline-block; 23 | } 24 | 25 | .searchDemo .haystackTextArea { 26 | width: 100%; 27 | height: 10rem; 28 | resize: none; 29 | } 30 | 31 | .searchDemo .arrayHaystack { 32 | margin: 1rem 0; 33 | } 34 | 35 | .searchDemo .arrayHaystack .arrayHaystackCharacters { 36 | white-space: nowrap; 37 | overflow-x: scroll; 38 | border: 1px solid #071a32; 39 | padding: 0.5rem; 40 | background: #f6f6f6; 41 | border-radius: 0.25rem; 42 | margin-top: 0.55rem; 43 | } 44 | -------------------------------------------------------------------------------- /src/algorithms/search/array/linear-search/demo/linear-search.demo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SearchPerformanceTest } from '../../demo-utils/search-performance-test.demo' 3 | import { linearSearch } from '../src/linear-search' 4 | 5 | export default { 6 | title: 'Algorithms/Search (Array)/Linear Search', 7 | } 8 | 9 | export const linearSearchPerformanceTest = () => ( 10 | 14 | ) 15 | -------------------------------------------------------------------------------- /src/algorithms/search/array/linear-search/src/linear-search.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Linear Search 3 | * 4 | * Starts at the beginning and iterates through the whole array 5 | * 6 | * Best case performance: Ω(1) (the element you're looking for is the first in the array) 7 | * Average case performance: 0(n) 8 | * Worst case performance: O(n) (the element you're looking for is the last in the array or not in the array at all) 9 | */ 10 | 11 | export const linearSearch = (haystack, needle, showLogs) => { 12 | if ( 13 | !(haystack instanceof Array) || 14 | typeof needle === 'undefined' || 15 | needle === null 16 | ) { 17 | return -1 18 | } 19 | 20 | for (let i = 0; i < haystack.length; i++) { 21 | /* istanbul ignore next */ 22 | showLogs && 23 | console.log( 24 | `iteration ${i + 1}: ${needle} === ${haystack[i]} ? ... ${ 25 | needle === haystack[i] 26 | }!` 27 | ) 28 | if (needle === haystack[i]) { 29 | return i 30 | } 31 | } 32 | return -1 33 | } 34 | -------------------------------------------------------------------------------- /src/algorithms/search/array/linear-search/src/linear-search.test.js: -------------------------------------------------------------------------------- 1 | import { linearSearch } from './linear-search' 2 | 3 | const showLogs = process.env.SHOW_LOGS === '1' 4 | 5 | describe('linearSearch', () => { 6 | it('returns the index when it finds an element that exists in the array', () => { 7 | const testArray = [1, 3, 8, 5, 9, 10, 12] 8 | expect(linearSearch(testArray, 8, showLogs)).toBe(2) 9 | }) 10 | 11 | it('returns the first index when it finds an element that exists in the array multiple times', () => { 12 | const testArray = ['c', 'b', 'd', 'c'] 13 | expect(linearSearch(testArray, 'c', showLogs)).toBe(0) 14 | }) 15 | 16 | it('returns -1 when it does not find the element it is looking for', () => { 17 | const testArray = [1, 3, 8, 5, 9, 10, 12] 18 | expect(linearSearch(testArray, 999, showLogs)).toBe(-1) 19 | }) 20 | 21 | it('handles a missing needle argument', () => { 22 | const testArray = [1, 3, 8, 5, 9, 10, 12] 23 | expect(linearSearch(testArray, undefined, showLogs)).toBe(-1) 24 | expect(linearSearch(testArray, null, showLogs)).toBe(-1) 25 | }) 26 | 27 | it('handles a non-array haystack argument', () => { 28 | expect(linearSearch('a string', 42, showLogs)).toBe(-1) 29 | expect(linearSearch(100, 42, showLogs)).toBe(-1) 30 | expect(linearSearch(null, 42, showLogs)).toBe(-1) 31 | expect(linearSearch(undefined, 42, showLogs)).toBe(-1) 32 | expect(linearSearch({ someKey: 'someValue' }, 42, showLogs)).toBe(-1) 33 | }) 34 | 35 | it('can search for a string in an array of strings', () => { 36 | const testArray = ['apple', 'banana', 'cat', 'dog', 'egg', 'fan', 'giraffe'] 37 | expect(linearSearch(testArray, 'cat', showLogs)).toBe(2) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /src/algorithms/search/string/additional-demos/search-performance-comparison-test.demo.css: -------------------------------------------------------------------------------- 1 | .searchComparisonDemo button, 2 | .searchComparisonDemo label { 3 | margin-bottom: 1rem; 4 | display: inline-block; 5 | } 6 | 7 | .searchComparisonDemo label span { 8 | margin-right: 1rem; 9 | } 10 | 11 | .searchComparisonDemo h3 { 12 | display: inline-block; 13 | margin: 0.5rem 0; 14 | } 15 | 16 | .searchComparisonDemo .haystackTextAreaLabel { 17 | width: 100%; 18 | } 19 | 20 | .searchComparisonDemo .haystackTextAreaLabel span { 21 | margin-bottom: 0.5rem; 22 | display: inline-block; 23 | } 24 | 25 | .searchComparisonDemo .haystackTextArea { 26 | width: 100%; 27 | height: 10rem; 28 | resize: none; 29 | } 30 | 31 | .searchComparisonDemo .stringHaystack { 32 | margin: 1rem 0; 33 | } 34 | 35 | .searchComparisonDemo .stringHaystack .stringHaystackCharacters { 36 | white-space: nowrap; 37 | overflow-x: scroll; 38 | border: 1px solid #071a32; 39 | padding: 0.5rem; 40 | background: #f6f6f6; 41 | border-radius: 0.25rem; 42 | margin-top: 0.55rem; 43 | } 44 | -------------------------------------------------------------------------------- /src/algorithms/search/string/additional-demos/search-performance-comparison-test.demo.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { naiveSearch } from '../naive-search/src/naive-search' 3 | import { boyerMooreHorspoolSearch } from '../boyer-moore-horspool-search/src/boyer-moore-horspool-search' 4 | import './search-performance-comparison-test.demo.css' 5 | import { declarationOfIndependence } from '../demo-utils/declaration-of-independence' 6 | 7 | export default { 8 | title: 'Algorithms/Search (String)/Search Comparisions', 9 | } 10 | 11 | class SearchPerformanceComparisonTest extends Component { 12 | state = { 13 | needle: '', 14 | haystack: declarationOfIndependence, 15 | lastResultSet: null, 16 | } 17 | 18 | handleNeedleChange = e => { 19 | this.setState({ needle: e.target.value }) 20 | } 21 | 22 | handleHaystackChange = e => { 23 | this.setState({ haystack: e.target.value }) 24 | } 25 | 26 | runPerformanceTest = e => { 27 | e.preventDefault() 28 | 29 | const { needle, haystack } = this.state 30 | 31 | const startTimeForNaiveSearch = performance.now() 32 | naiveSearch(haystack, needle) 33 | const endTimeForNaiveSearch = performance.now() 34 | 35 | const startTimeForBoyerMooreHorspoolSearch = performance.now() 36 | const result = boyerMooreHorspoolSearch(haystack, needle) 37 | const endTimeForBoyerMooreHorspoolSearch = performance.now() 38 | 39 | this.setState({ 40 | lastResultSet: { 41 | haystack, 42 | needle, 43 | result, 44 | timeTaken: { 45 | naiveSearch: endTimeForNaiveSearch - startTimeForNaiveSearch, 46 | boyerMooreHorspoolSearch: 47 | endTimeForBoyerMooreHorspoolSearch - 48 | startTimeForBoyerMooreHorspoolSearch, 49 | }, 50 | }, 51 | }) 52 | } 53 | 54 | render() { 55 | const { needle, haystack, lastResultSet } = this.state 56 | 57 | return ( 58 |
59 |

Performance Test Comparing Various Searching Algorithms

60 |
61 | 69 |
70 |