├── .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 |
83 |
84 |
Performance Results:
85 | {lastResultSet && (
86 |
87 |
88 | Searching the string for{' '}
89 | "{lastResultSet.needle}". The string was{' '}
90 |
91 | {lastResultSet.result === -1
92 | ? 'not found'
93 | : `found at index ${lastResultSet.result}`}
94 |
95 | .
96 |
97 |
98 |
99 |
Naive Search
took{' '}
100 | {lastResultSet.timeTaken.naiveSearch.toFixed(3)}{' '}
101 | milliseconds (or{' '}
102 | {(lastResultSet.timeTaken.naiveSearch / 1000).toFixed(6)}{' '}
103 | seconds).
104 |
105 |
106 |
107 |
Boyer-Moore-Horspool Search
took{' '}
108 |
109 | {lastResultSet.timeTaken.boyerMooreHorspoolSearch.toFixed(3)}
110 | {' '}
111 | milliseconds (or{' '}
112 |
113 | {(
114 | lastResultSet.timeTaken.boyerMooreHorspoolSearch / 1000
115 | ).toFixed(6)}
116 | {' '}
117 | seconds).
118 |
119 |
120 |
121 |
String Haystack:{' '}
122 |
{haystack}
123 |
124 |
125 | )}
126 |
127 |
128 | )
129 | }
130 | }
131 |
132 | export const performanceTest = () =>
133 |
--------------------------------------------------------------------------------
/src/algorithms/search/string/boyer-moore-horspool-search/demo/boyer-moore-horspool-search.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SearchPerformanceTest } from '../../demo-utils/search-performance-test.demo'
3 | import { boyerMooreHorspoolSearch } from '../src/boyer-moore-horspool-search'
4 |
5 | export default {
6 | title: 'Algorithms/Search (String)/Boyer-Moore-Horspool Search',
7 | }
8 |
9 | export const boyerMooreHorspoolSearchPerformanceTest = () => (
10 |
14 | )
15 |
--------------------------------------------------------------------------------
/src/algorithms/search/string/boyer-moore-horspool-search/src/boyer-moore-horspool-search.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Boyer-Moore-Horspool Search
3 | *
4 | * Tries to be more efficient by skipping characters when it can.
5 | *
6 | * You go through the haystack from left to right, but you start
7 | * matching from the end of the string to find (the needle).
8 | *
9 | * If the last character of the string in the current selection in the
10 | * haystack isn’t found anywhere in the needle, you can move where you’re
11 | * searching forward by the entire length of the needle.
12 | *
13 | * If the last character of the string in the current selection in the
14 | * haystack IS found somewhere in the needle, but it’s not a match with the
15 | * last character in the needle, then you can move where you’re searching
16 | * forward by the number of indexes that character is from the end of your
17 | * needle string.
18 | *
19 | * If the last character of the string in the current selection in the
20 | * haystack matches the last character in the needle string, the you
21 | * iterate backwards from right of left over the needle string, checking
22 | * for matches. If they all match, you found it! If you get a mismatch,
23 | * you move where you’re searching forward again.
24 | *
25 | * Performance improves with the length of the search string, because
26 | * that means you can potentially skip more characters each time a bad
27 | * match occurs.
28 | *
29 | * This algorithm is appropriate as a general purpose string search algorithm.
30 | *
31 | * Best case performance: Ω(n/m), where n is the length of the string to search and m is the length of the string to find
32 | * Average case performance: 0(n)
33 | * Worst case performance: O(n*m), where n is the length of the string to search and m is the length of the string to find
34 | */
35 |
36 | export const boyerMooreHorspoolSearch = (haystack, needle, showLogs) => {
37 | if (typeof haystack !== 'string' || typeof needle !== 'string') {
38 | /* istanbul ignore next */
39 | showLogs && console.log('bad input, exiting early')
40 | return -1
41 | }
42 |
43 | const needleLength = needle.length
44 | let haystackRemainingLength = haystack.length
45 |
46 | if (needleLength > haystackRemainingLength) {
47 | /* istanbul ignore next */
48 | showLogs && console.log('needle is longer than the haystack, exiting early')
49 | return -1
50 | }
51 |
52 | // first loop through the needle once to
53 | // create the mismatch table so you know how much
54 | // to offset by for each character on mismatch
55 | const lastIndexOfNeedle = needleLength - 1
56 | const mismatchTable = {}
57 | for (let i = 0; i < needleLength; i++) {
58 | mismatchTable[needle[i]] = lastIndexOfNeedle - i
59 | }
60 |
61 | let jumpAmount
62 | let haystackOffset = 0
63 | let currentIndex = 0
64 |
65 | // loop through the haystack from beginning to end
66 | while (haystackRemainingLength >= needleLength) {
67 | // loop through the needle, starting at the end and moving backward
68 | for (
69 | currentIndex = lastIndexOfNeedle;
70 | haystack[haystackOffset + currentIndex] === needle[currentIndex];
71 | currentIndex--
72 | ) {
73 | // if you've gotten all the way to the front of the needle,
74 | // then you've found an exact match. you're done!
75 | if (currentIndex === 0) {
76 | return haystackOffset
77 | }
78 | }
79 |
80 | // if you're here, that means we got a mismatch and can jump further down the haystack
81 | jumpAmount =
82 | mismatchTable[haystack[haystackOffset + lastIndexOfNeedle]] ||
83 | needleLength
84 | haystackRemainingLength -= jumpAmount
85 | haystackOffset += jumpAmount
86 | }
87 |
88 | // needle was not found in the haystack
89 | return -1
90 | }
91 |
--------------------------------------------------------------------------------
/src/algorithms/search/string/boyer-moore-horspool-search/src/boyer-moore-horspool-search.test.js:
--------------------------------------------------------------------------------
1 | import { boyerMooreHorspoolSearch } from './boyer-moore-horspool-search'
2 |
3 | const showLogs = process.env.SHOW_LOGS === '1'
4 |
5 | describe('boyerMooreHorspoolSearch', () => {
6 | it('handles a non-string needle argument', () => {
7 | expect(boyerMooreHorspoolSearch('search me', undefined, showLogs)).toBe(-1)
8 | expect(boyerMooreHorspoolSearch('search me', null, showLogs)).toBe(-1)
9 | expect(boyerMooreHorspoolSearch('search me', 42, showLogs)).toBe(-1)
10 | expect(boyerMooreHorspoolSearch('search me', true, showLogs)).toBe(-1)
11 | expect(
12 | boyerMooreHorspoolSearch('search me', { someKey: 'someVal' }, showLogs)
13 | ).toBe(-1)
14 | })
15 |
16 | it('handles a non-string haystack argument', () => {
17 | expect(boyerMooreHorspoolSearch(undefined, 'find me', showLogs)).toBe(-1)
18 | expect(boyerMooreHorspoolSearch(null, 'find me', showLogs)).toBe(-1)
19 | expect(boyerMooreHorspoolSearch(42, 'find me', showLogs)).toBe(-1)
20 | expect(boyerMooreHorspoolSearch(true, 'find me', showLogs)).toBe(-1)
21 | expect(
22 | boyerMooreHorspoolSearch({ someKey: 'someVal' }, 'find me', showLogs)
23 | ).toBe(-1)
24 | })
25 |
26 | it('returns -1 if the string is not found', () => {
27 | expect(boyerMooreHorspoolSearch('blah', 'hey', showLogs)).toBe(-1)
28 | })
29 |
30 | it('returns -1 if the needle is longer than the haystack', () => {
31 | expect(
32 | boyerMooreHorspoolSearch(
33 | 'small haystack',
34 | 'really long needle to search for'
35 | )
36 | ).toBe(-1)
37 | })
38 |
39 | it('can correctly find a substring in a string with no spaces', () => {
40 | const haystack = 'abcdefghijklmnopqrstuvwxyz'
41 | const needle = 'ijk'
42 | expect(boyerMooreHorspoolSearch(haystack, needle)).toBe(8)
43 | })
44 |
45 | it('can correctly find a substring in a string with spaces', () => {
46 | const haystack = 'look at this nice short string'
47 | const needle = 'at'
48 | expect(boyerMooreHorspoolSearch(haystack, needle)).toBe(5)
49 | })
50 |
51 | it('returns the index of the first instance of the string found if there are duplicates', () => {
52 | const haystack = 'ababababababababab'
53 | const needle = 'ba'
54 | expect(boyerMooreHorspoolSearch(haystack, needle)).toBe(1)
55 | })
56 | })
57 |
--------------------------------------------------------------------------------
/src/algorithms/search/string/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 .stringHaystack {
32 | margin: 1rem 0;
33 | }
34 |
35 | .searchDemo .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/demo-utils/search-performance-test.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import './search-performance-test.demo.css'
3 | import { declarationOfIndependence } from './declaration-of-independence'
4 |
5 | export class SearchPerformanceTest extends Component {
6 | state = {
7 | needle: '',
8 | haystack: declarationOfIndependence,
9 | lastResultSet: null,
10 | }
11 |
12 | handleNeedleChange = e => {
13 | this.setState({ needle: e.target.value })
14 | }
15 |
16 | handleHaystackChange = e => {
17 | this.setState({ haystack: e.target.value })
18 | }
19 |
20 | runPerformanceTest = e => {
21 | e.preventDefault()
22 |
23 | const { needle, haystack } = this.state
24 | const { searchMethod } = this.props
25 |
26 | const startTime = performance.now()
27 | const result = searchMethod(haystack, needle)
28 | const endTime = performance.now()
29 |
30 | this.setState({
31 | lastResultSet: {
32 | haystack,
33 | needle,
34 | result,
35 | timeTaken: endTime - startTime,
36 | },
37 | })
38 | }
39 |
40 | render() {
41 | const { needle, haystack, lastResultSet } = this.state
42 | const { searchMethodName } = this.props
43 |
44 | return (
45 |
46 |
{searchMethodName} Performance Test
47 |
70 |
71 |
{searchMethodName} Performance Results:
72 | {lastResultSet && (
73 |
74 |
75 | Searching the string for{' '}
76 | "{lastResultSet.needle}" using a{' '}
77 | {searchMethodName} took{' '}
78 | {lastResultSet.timeTaken.toFixed(3)} milliseconds (or{' '}
79 | {(lastResultSet.timeTaken / 1000).toFixed(6)} seconds).
80 |
81 |
82 | The string was{' '}
83 |
84 | {lastResultSet.result === -1
85 | ? 'not found'
86 | : `found at index ${lastResultSet.result}`}
87 |
88 | .
89 |
90 |
91 |
92 |
String Haystack:{' '}
93 |
{haystack}
94 |
95 |
96 | )}
97 |
98 |
99 | )
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/algorithms/search/string/naive-search/demo/naive-search.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SearchPerformanceTest } from '../../demo-utils/search-performance-test.demo'
3 | import { naiveSearch } from '../src/naive-search'
4 |
5 | export default {
6 | title: 'Algorithms/Search (String)/Naive Search',
7 | }
8 |
9 | export const naiveSearchPerformanceTest = () => (
10 |
14 | )
15 |
--------------------------------------------------------------------------------
/src/algorithms/search/string/naive-search/src/naive-search.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Naive Search
3 | *
4 | * Starts at the beginning and iterates through the whole string
5 | *
6 | * Best case performance: Ω(1) (the substring you're looking for is the start of the string)
7 | * Average case performance: 0(n+m)
8 | * Worst case performance: O(n*m), where the length of the pattern is m and the length of the search string is n
9 | * (the substring you're looking for is the end of the string or not in the string at all)
10 | */
11 |
12 | export const naiveSearch = (haystack, needle, showLogs) => {
13 | if (typeof haystack !== 'string' || typeof needle !== 'string') {
14 | /* istanbul ignore next */
15 | showLogs && console.log('bad input, exiting early')
16 | return -1
17 | }
18 |
19 | if (needle.length > haystack.length) {
20 | /* istanbul ignore next */
21 | showLogs && console.log('needle is longer than the haystack, exiting early')
22 | return -1
23 | }
24 |
25 | for (let i = 0; i < haystack.length; i++) {
26 | for (let j = 0; j < needle.length; j++) {
27 | if (haystack[i + j] !== needle[j]) {
28 | break
29 | }
30 |
31 | if (j === needle.length - 1) {
32 | return i
33 | }
34 | }
35 | }
36 |
37 | return -1
38 | }
39 |
--------------------------------------------------------------------------------
/src/algorithms/search/string/naive-search/src/naive-search.test.js:
--------------------------------------------------------------------------------
1 | import { naiveSearch } from './naive-search'
2 |
3 | const showLogs = process.env.SHOW_LOGS === '1'
4 |
5 | describe('naiveSearch', () => {
6 | it('handles a non-string needle argument', () => {
7 | expect(naiveSearch('search me', undefined, showLogs)).toBe(-1)
8 | expect(naiveSearch('search me', null, showLogs)).toBe(-1)
9 | expect(naiveSearch('search me', 42, showLogs)).toBe(-1)
10 | expect(naiveSearch('search me', true, showLogs)).toBe(-1)
11 | expect(naiveSearch('search me', { someKey: 'someVal' }, showLogs)).toBe(-1)
12 | })
13 |
14 | it('handles a non-string haystack argument', () => {
15 | expect(naiveSearch(undefined, 'find me', showLogs)).toBe(-1)
16 | expect(naiveSearch(null, 'find me', showLogs)).toBe(-1)
17 | expect(naiveSearch(42, 'find me', showLogs)).toBe(-1)
18 | expect(naiveSearch(true, 'find me', showLogs)).toBe(-1)
19 | expect(naiveSearch({ someKey: 'someVal' }, 'find me', showLogs)).toBe(-1)
20 | })
21 |
22 | it('returns -1 if the string is not found', () => {
23 | expect(naiveSearch('blah', 'hey', showLogs)).toBe(-1)
24 | })
25 |
26 | it('returns -1 if the needle is longer than the haystack', () => {
27 | expect(
28 | naiveSearch('small haystack', 'really long needle to search for')
29 | ).toBe(-1)
30 | })
31 |
32 | it('can correctly find a substring in a string with no spaces', () => {
33 | const haystack = 'abcdefghijklmnopqrstuvwxyz'
34 | const needle = 'ijk'
35 | expect(naiveSearch(haystack, needle)).toBe(8)
36 | })
37 |
38 | it('can correctly find a substring in a string with spaces', () => {
39 | const haystack = 'look at this nice short string'
40 | const needle = 'at'
41 | expect(naiveSearch(haystack, needle)).toBe(5)
42 | })
43 |
44 | it('returns the index of the first instance of the string found if there are duplicates', () => {
45 | const haystack = 'ababababababababab'
46 | const needle = 'ba'
47 | expect(naiveSearch(haystack, needle)).toBe(1)
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/src/algorithms/set/additional-demos/view-all.demo.css:
--------------------------------------------------------------------------------
1 | .viewAllDemo h2 {
2 | font-size: 1rem;
3 | }
4 |
5 | .viewAllDemo button {
6 | margin-bottom: 0.5rem;
7 | }
8 |
9 | .viewAllDemo label {
10 | margin-bottom: 0.5rem;
11 | display: inline-block;
12 | }
13 |
14 | .viewAllDemo label span {
15 | margin-right: 1rem;
16 | }
17 |
--------------------------------------------------------------------------------
/src/algorithms/set/additional-demos/view-all.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Set } from '../../../data-structures/set/src/set'
3 | import { union } from '../union/src/union'
4 | import { intersection } from '../intersection/src/intersection'
5 | import { setDifference } from '../set-difference/src/set-difference'
6 | import { symmetricDifference } from '../symmetric-difference/src/symmetric-difference'
7 | import './view-all.demo.css'
8 |
9 | export default {
10 | title: 'Algorithms/Set/View All Side by Side',
11 | }
12 |
13 | class ViewAllSetAlgorithms extends Component {
14 | state = {
15 | set1Csv: '',
16 | set2Csv: '',
17 | }
18 |
19 | handleSet1CsvChange = e => {
20 | this.setState({ set1Csv: e.target.value })
21 | }
22 |
23 | handleSet2CsvChange = e => {
24 | this.setState({ set2Csv: e.target.value })
25 | }
26 |
27 | renderOutput = () => {
28 | const { set1Csv, set2Csv } = this.state
29 |
30 | const set1 = new Set()
31 | set1Csv.split(',').forEach(val => {
32 | if (val.trim()) {
33 | set1.add(val.trim())
34 | }
35 | })
36 |
37 | const set2 = new Set()
38 | set2Csv.split(',').forEach(val => {
39 | if (val.trim()) {
40 | set2.add(val.trim())
41 | }
42 | })
43 |
44 | return (
45 |
46 |
Union of Set 1 and Set 2:
47 |
{union(set1, set2).enumerate().join(', ')}
48 |
49 |
Intersection of Set 1 and Set 2:
50 |
{intersection(set1, set2).enumerate().join(', ')}
51 |
52 |
Set Difference of Set 1 and Set 2:
53 |
{setDifference(set1, set2).enumerate().join(', ')}
54 |
55 |
Set Difference of Set 2 and Set 1:
56 |
{setDifference(set2, set1).enumerate().join(', ')}
57 |
58 |
Symmetric Difference of Set 1 and Set 2:
59 |
{symmetricDifference(set1, set2).enumerate().join(', ')}
60 |
61 | )
62 | }
63 |
64 | render() {
65 | const { set1Csv, set2Csv } = this.state
66 |
67 | return (
68 |
69 |
Set Algorithm Comparison Demo
70 |
71 | Enter comma-separated values into each text input to add items to each
72 | set.
73 |
74 |
95 |
{this.renderOutput()}
96 |
97 | )
98 | }
99 | }
100 |
101 | export const viewAllSetAlgorithms = () =>
102 |
--------------------------------------------------------------------------------
/src/algorithms/set/intersection/demo/intersection.demo.css:
--------------------------------------------------------------------------------
1 | .intersectionDemo h2 {
2 | font-size: 1rem;
3 | }
4 |
5 | .intersectionDemo button {
6 | margin-bottom: 0.5rem;
7 | }
8 |
9 | .intersectionDemo label {
10 | margin-bottom: 0.5rem;
11 | display: inline-block;
12 | }
13 |
14 | .intersectionDemo label span {
15 | margin-right: 1rem;
16 | }
17 |
--------------------------------------------------------------------------------
/src/algorithms/set/intersection/demo/intersection.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Set } from '../../../../data-structures/set/src/set'
3 | import { intersection } from '../src/intersection'
4 | import './intersection.demo.css'
5 |
6 | export default {
7 | title: 'Algorithms/Set/Intersection',
8 | }
9 |
10 | class IntersectionOfTwoSetsVisualizer extends Component {
11 | state = {
12 | set1Csv: '',
13 | set2Csv: '',
14 | }
15 |
16 | handleSet1CsvChange = e => {
17 | this.setState({ set1Csv: e.target.value })
18 | }
19 |
20 | handleSet2CsvChange = e => {
21 | this.setState({ set2Csv: e.target.value })
22 | }
23 |
24 | findIntersection = () => {
25 | const { set1Csv, set2Csv } = this.state
26 |
27 | const set1 = new Set()
28 | set1Csv.split(',').forEach(val => {
29 | if (val.trim()) {
30 | set1.add(val.trim())
31 | }
32 | })
33 |
34 | const set2 = new Set()
35 | set2Csv.split(',').forEach(val => {
36 | if (val.trim()) {
37 | set2.add(val.trim())
38 | }
39 | })
40 |
41 | return {intersection(set1, set2).enumerate().join(', ')}
42 | }
43 |
44 | render() {
45 | const { set1Csv, set2Csv } = this.state
46 |
47 | return (
48 |
49 |
Intersection of Two Sets Demo
50 |
51 | Enter comma-separated values into each text input to add items to each
52 | set.
53 |
54 |
75 |
76 |
Intersection of Set 1 and Set 2:
77 | {this.findIntersection()}
78 |
79 |
80 | )
81 | }
82 | }
83 |
84 | export const intersectionOfTwoSetsVisualizer = () => (
85 |
86 | )
87 |
--------------------------------------------------------------------------------
/src/algorithms/set/intersection/src/intersection.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Intersection
3 | *
4 | * Compares two sets and produces a third set that contains all of the
5 | * intersecting/matching values that are found in both sets
6 | *
7 | * Ex. Intersection of { 1, 2, 3 } and { 2, 3, 4 } is { 2, 3 }
8 | *
9 | * Performance: Quadratic - O(n * m), where n is the length of set 1 and m is the length of set 2
10 | * Note: this is sort of like O(n^2) performance
11 | */
12 |
13 | import { Set } from '../../../../data-structures/set/src/set'
14 |
15 | export const intersection = (set1, set2) => {
16 | const intersectionSet = new Set()
17 |
18 | set1.enumerate().forEach(val => {
19 | if (set2.has(val)) {
20 | intersectionSet.add(val)
21 | }
22 | })
23 |
24 | set2.enumerate().forEach(val => {
25 | if (set1.has(val)) {
26 | intersectionSet.add(val)
27 | }
28 | })
29 |
30 | return intersectionSet
31 | }
32 |
--------------------------------------------------------------------------------
/src/algorithms/set/intersection/src/intersection.test.js:
--------------------------------------------------------------------------------
1 | import { intersection } from './intersection'
2 | import { Set } from '../../../../data-structures/set/src/set'
3 |
4 | describe('intersection', () => {
5 | it('creates a third set from the two given sets, using only values that are present in both sets', () => {
6 | const set1 = new Set()
7 | set1.add(42)
8 | set1.add(10)
9 | set1.add('a')
10 |
11 | const set2 = new Set()
12 | set2.add(42)
13 | set2.add('a')
14 | set2.add('b')
15 | set2.add('c')
16 |
17 | const set3 = intersection(set1, set2)
18 |
19 | expect(set3.size()).toBe(2)
20 | expect(set3.enumerate()).toEqual([42, 'a'])
21 | })
22 |
23 | it('only adds values that are present in both sets', () => {
24 | const set1 = new Set()
25 | set1.add(42)
26 | set1.add(10)
27 |
28 | const set2 = new Set()
29 | set2.add('a')
30 | set2.add('b')
31 | set2.add('c')
32 |
33 | const set3 = intersection(set1, set2)
34 |
35 | expect(set3.size()).toBe(0)
36 | expect(set3.enumerate()).toEqual([])
37 | })
38 |
39 | it('does not modify the original two sets', () => {
40 | const set1 = new Set()
41 | set1.add(42)
42 | set1.add(10)
43 | set1.add('a')
44 |
45 | const set2 = new Set()
46 | set2.add(42)
47 | set2.add('a')
48 | set2.add('b')
49 | set2.add('c')
50 |
51 | const set3 = intersection(set1, set2)
52 |
53 | expect(set3.size()).toBe(2)
54 | expect(set3.enumerate()).toEqual([42, 'a'])
55 |
56 | expect(set1.size()).toBe(3)
57 | expect(set1.enumerate()).toEqual([42, 10, 'a'])
58 |
59 | expect(set2.size()).toBe(4)
60 | expect(set2.enumerate()).toEqual([42, 'a', 'b', 'c'])
61 | })
62 |
63 | it('can handle creating the intersection of two empty sets', () => {
64 | const set1 = new Set()
65 | const set2 = new Set()
66 | const set3 = intersection(set1, set2)
67 |
68 | expect(set3.size()).toBe(0)
69 | expect(set3.enumerate()).toEqual([])
70 | })
71 | })
72 |
--------------------------------------------------------------------------------
/src/algorithms/set/set-difference/demo/set-difference.demo.css:
--------------------------------------------------------------------------------
1 | .setDifferenceDemo h2 {
2 | font-size: 1rem;
3 | }
4 |
5 | .setDifferenceDemo button {
6 | margin-bottom: 0.5rem;
7 | }
8 |
9 | .setDifferenceDemo label {
10 | margin-bottom: 0.5rem;
11 | display: inline-block;
12 | }
13 |
14 | .setDifferenceDemo label span {
15 | margin-right: 1rem;
16 | }
17 |
--------------------------------------------------------------------------------
/src/algorithms/set/set-difference/demo/set-difference.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Set } from '../../../../data-structures/set/src/set'
3 | import { setDifference } from '../src/set-difference'
4 | import './set-difference.demo.css'
5 |
6 | export default {
7 | title: 'Algorithms/Set/Set Difference',
8 | }
9 |
10 | class SetDifferenceOfTwoSetsVisualizer extends Component {
11 | state = {
12 | set1Csv: '',
13 | set2Csv: '',
14 | }
15 |
16 | handleSet1CsvChange = e => {
17 | this.setState({ set1Csv: e.target.value })
18 | }
19 |
20 | handleSet2CsvChange = e => {
21 | this.setState({ set2Csv: e.target.value })
22 | }
23 |
24 | findSetDifferences = () => {
25 | const { set1Csv, set2Csv } = this.state
26 |
27 | const set1 = new Set()
28 | set1Csv.split(',').forEach(val => {
29 | if (val.trim()) {
30 | set1.add(val.trim())
31 | }
32 | })
33 |
34 | const set2 = new Set()
35 | set2Csv.split(',').forEach(val => {
36 | if (val.trim()) {
37 | set2.add(val.trim())
38 | }
39 | })
40 |
41 | return (
42 |
43 |
Set Difference of Set 1 and Set 2:
44 |
{setDifference(set1, set2).enumerate().join(', ')}
45 |
46 |
Set Difference of Set 2 and Set 1:
47 |
{setDifference(set2, set1).enumerate().join(', ')}
48 |
49 | )
50 | }
51 |
52 | render() {
53 | const { set1Csv, set2Csv } = this.state
54 |
55 | return (
56 |
57 |
Set Difference of Two Sets Demo
58 |
59 | Enter comma-separated values into each text input to add items to each
60 | set.
61 |
62 |
83 |
{this.findSetDifferences()}
84 |
85 | )
86 | }
87 | }
88 |
89 | export const setDifferenceOfTwoSetsVisualizer = () => (
90 |
91 | )
92 |
--------------------------------------------------------------------------------
/src/algorithms/set/set-difference/src/set-difference.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set Difference
3 | *
4 | * Compares two sets and returns all items of the first set that are not members of the second set
5 | *
6 | * Ex. Set difference of { 2, 3, 4 } and { 3, 4, 5 } is { 2 }
7 | *
8 | * Performance: Quadratic - O(n * m), where n is the length of set 1 and m is the length of set 2
9 | * Note: this is sort of like O(n^2) performance
10 | */
11 |
12 | import { Set } from '../../../../data-structures/set/src/set'
13 |
14 | export const setDifference = (set1, set2) => {
15 | const setDifferenceSet = new Set()
16 |
17 | set1.enumerate().forEach(val => {
18 | if (!set2.has(val)) {
19 | setDifferenceSet.add(val)
20 | }
21 | })
22 |
23 | return setDifferenceSet
24 | }
25 |
--------------------------------------------------------------------------------
/src/algorithms/set/set-difference/src/set-difference.test.js:
--------------------------------------------------------------------------------
1 | import { setDifference } from './set-difference'
2 | import { Set } from '../../../../data-structures/set/src/set'
3 |
4 | describe('setDifference', () => {
5 | it('creates a third set from the two given sets, using only values that are present in only the first set', () => {
6 | const set1 = new Set()
7 | set1.add(42)
8 | set1.add(10)
9 | set1.add(5)
10 | set1.add('a')
11 |
12 | const set2 = new Set()
13 | set2.add(42)
14 | set2.add('a')
15 | set2.add('b')
16 | set2.add('c')
17 |
18 | const set3 = setDifference(set1, set2)
19 |
20 | expect(set3.size()).toBe(2)
21 | expect(set3.enumerate()).toEqual([10, 5])
22 | })
23 |
24 | it('does not modify the original two sets', () => {
25 | const set1 = new Set()
26 | set1.add(42)
27 | set1.add(10)
28 | set1.add(5)
29 | set1.add('a')
30 |
31 | const set2 = new Set()
32 | set2.add(42)
33 | set2.add('a')
34 | set2.add('b')
35 | set2.add('c')
36 |
37 | const set3 = setDifference(set1, set2)
38 |
39 | expect(set3.size()).toBe(2)
40 | expect(set3.enumerate()).toEqual([10, 5])
41 |
42 | expect(set1.size()).toBe(4)
43 | expect(set1.enumerate()).toEqual([42, 10, 5, 'a'])
44 |
45 | expect(set2.size()).toBe(4)
46 | expect(set2.enumerate()).toEqual([42, 'a', 'b', 'c'])
47 | })
48 |
49 | it('can handle creating the setDifference of two empty sets', () => {
50 | const set1 = new Set()
51 | const set2 = new Set()
52 | const set3 = setDifference(set1, set2)
53 |
54 | expect(set3.size()).toBe(0)
55 | expect(set3.enumerate()).toEqual([])
56 | })
57 |
58 | it('handles the order that the sets are provided in correctly', () => {
59 | const set1 = new Set()
60 | set1.add(42)
61 | set1.add('a')
62 |
63 | const set2 = new Set()
64 | set2.add('a')
65 |
66 | const set3 = setDifference(set1, set2)
67 | expect(set3.size()).toBe(1)
68 | expect(set3.enumerate()).toEqual([42])
69 |
70 | const set4 = setDifference(set2, set1)
71 | expect(set4.size()).toBe(0)
72 | expect(set4.enumerate()).toEqual([])
73 | })
74 | })
75 |
--------------------------------------------------------------------------------
/src/algorithms/set/symmetric-difference/demo/symmetric-difference.demo.css:
--------------------------------------------------------------------------------
1 | .symmetricDifferenceDemo h2 {
2 | font-size: 1rem;
3 | }
4 |
5 | .symmetricDifferenceDemo button {
6 | margin-bottom: 0.5rem;
7 | }
8 |
9 | .symmetricDifferenceDemo label {
10 | margin-bottom: 0.5rem;
11 | display: inline-block;
12 | }
13 |
14 | .symmetricDifferenceDemo label span {
15 | margin-right: 1rem;
16 | }
17 |
--------------------------------------------------------------------------------
/src/algorithms/set/symmetric-difference/demo/symmetric-difference.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Set } from '../../../../data-structures/set/src/set'
3 | import { symmetricDifference } from '../src/symmetric-difference'
4 | import './symmetric-difference.demo.css'
5 |
6 | export default {
7 | title: 'Algorithms/Set/Symmetric Difference',
8 | }
9 |
10 | class SymmetricDifferenceOfTwoSetsVisualizer extends Component {
11 | state = {
12 | set1Csv: '',
13 | set2Csv: '',
14 | }
15 |
16 | handleSet1CsvChange = e => {
17 | this.setState({ set1Csv: e.target.value })
18 | }
19 |
20 | handleSet2CsvChange = e => {
21 | this.setState({ set2Csv: e.target.value })
22 | }
23 |
24 | findSymmetricDifference = () => {
25 | const { set1Csv, set2Csv } = this.state
26 |
27 | const set1 = new Set()
28 | set1Csv.split(',').forEach(val => {
29 | if (val.trim()) {
30 | set1.add(val.trim())
31 | }
32 | })
33 |
34 | const set2 = new Set()
35 | set2Csv.split(',').forEach(val => {
36 | if (val.trim()) {
37 | set2.add(val.trim())
38 | }
39 | })
40 |
41 | return {symmetricDifference(set1, set2).enumerate().join(', ')}
42 | }
43 |
44 | render() {
45 | const { set1Csv, set2Csv } = this.state
46 |
47 | return (
48 |
49 |
Symmetric Difference of Two Sets Demo
50 |
51 | Enter comma-separated values into each text input to add items to each
52 | set.
53 |
54 |
75 |
76 |
Symmetric Difference of Set 1 and Set 2:
77 | {this.findSymmetricDifference()}
78 |
79 |
80 | )
81 | }
82 | }
83 |
84 | export const symmetricDifferenceOfTwoSetsVisualizer = () => (
85 |
86 | )
87 |
--------------------------------------------------------------------------------
/src/algorithms/set/symmetric-difference/src/symmetric-difference.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Symmetric Difference
3 | *
4 | * Compares two sets and returns all of the items that exist in only one of the two sets
5 | * The symmetric difference is the set difference of the union and intersection of the input sets
6 | *
7 | * Ex. Symmetric difference of { 2, 3, 4 } and { 3, 4, 5 } is { 2, 5 }
8 | *
9 | * Performance: Quadratic - O(n * m), where n is the length of set 1 and m is the length of set 2
10 | * Note: this is sort of like O(n^2) performance
11 | */
12 |
13 | import { setDifference } from '../../set-difference/src/set-difference'
14 | import { intersection } from '../../intersection/src/intersection'
15 | import { union } from '../../union/src/union'
16 |
17 | export const symmetricDifference = (set1, set2) => {
18 | const intersectionSet = intersection(set1, set2)
19 | const unionSet = union(set1, set2)
20 | return setDifference(unionSet, intersectionSet)
21 | }
22 |
--------------------------------------------------------------------------------
/src/algorithms/set/symmetric-difference/src/symmetric-difference.test.js:
--------------------------------------------------------------------------------
1 | import { symmetricDifference } from './symmetric-difference'
2 | import { Set } from '../../../../data-structures/set/src/set'
3 |
4 | describe('symmetricDifference', () => {
5 | it('creates a third set from the two given sets, using only values that are present in only one of the sets', () => {
6 | const set1 = new Set()
7 | set1.add(2)
8 | set1.add(3)
9 | set1.add(4)
10 |
11 | const set2 = new Set()
12 | set2.add(3)
13 | set2.add(4)
14 | set2.add(5)
15 |
16 | const set3 = symmetricDifference(set1, set2)
17 |
18 | expect(set3.size()).toBe(2)
19 | expect(set3.enumerate()).toEqual([2, 5])
20 | })
21 |
22 | it('does not modify the original two sets', () => {
23 | const set1 = new Set()
24 | set1.add(2)
25 | set1.add(3)
26 | set1.add(4)
27 |
28 | const set2 = new Set()
29 | set2.add(3)
30 | set2.add(4)
31 | set2.add(5)
32 |
33 | const set3 = symmetricDifference(set1, set2)
34 |
35 | expect(set3.size()).toBe(2)
36 | expect(set3.enumerate()).toEqual([2, 5])
37 |
38 | expect(set1.size()).toBe(3)
39 | expect(set1.enumerate()).toEqual([2, 3, 4])
40 |
41 | expect(set2.size()).toBe(3)
42 | expect(set2.enumerate()).toEqual([3, 4, 5])
43 | })
44 |
45 | it('can handle creating the symmetricDifference of two empty sets', () => {
46 | const set1 = new Set()
47 | const set2 = new Set()
48 | const set3 = symmetricDifference(set1, set2)
49 |
50 | expect(set3.size()).toBe(0)
51 | expect(set3.enumerate()).toEqual([])
52 | })
53 |
54 | it('returns the same set of items, regardless of the order the sets are provided to the method', () => {
55 | const set1 = new Set()
56 | set1.add(2)
57 | set1.add(3)
58 | set1.add(4)
59 |
60 | const set2 = new Set()
61 | set2.add(3)
62 | set2.add(4)
63 | set2.add(5)
64 |
65 | const set3 = symmetricDifference(set1, set2)
66 | expect(set3.size()).toBe(2)
67 | expect(set3.enumerate()).toEqual([2, 5])
68 |
69 | const set4 = symmetricDifference(set2, set1)
70 | expect(set4.size()).toBe(2)
71 | expect(set4.enumerate()).toEqual([5, 2])
72 | })
73 | })
74 |
--------------------------------------------------------------------------------
/src/algorithms/set/union/demo/union.demo.css:
--------------------------------------------------------------------------------
1 | .unionDemo h2 {
2 | font-size: 1rem;
3 | }
4 |
5 | .unionDemo button {
6 | margin-bottom: 0.5rem;
7 | }
8 |
9 | .unionDemo label {
10 | margin-bottom: 0.5rem;
11 | display: inline-block;
12 | }
13 |
14 | .unionDemo label span {
15 | margin-right: 1rem;
16 | }
17 |
--------------------------------------------------------------------------------
/src/algorithms/set/union/demo/union.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Set } from '../../../../data-structures/set/src/set'
3 | import { union } from '../src/union'
4 | import './union.demo.css'
5 |
6 | export default {
7 | title: 'Algorithms/Set/Union',
8 | }
9 |
10 | class UnionOfTwoSetsVisualizer extends Component {
11 | state = {
12 | set1Csv: '',
13 | set2Csv: '',
14 | }
15 |
16 | handleSet1CsvChange = e => {
17 | this.setState({ set1Csv: e.target.value })
18 | }
19 |
20 | handleSet2CsvChange = e => {
21 | this.setState({ set2Csv: e.target.value })
22 | }
23 |
24 | findUnion = () => {
25 | const { set1Csv, set2Csv } = this.state
26 |
27 | const set1 = new Set()
28 | set1Csv.split(',').forEach(val => {
29 | if (val.trim()) {
30 | set1.add(val.trim())
31 | }
32 | })
33 |
34 | const set2 = new Set()
35 | set2Csv.split(',').forEach(val => {
36 | if (val.trim()) {
37 | set2.add(val.trim())
38 | }
39 | })
40 |
41 | return {union(set1, set2).enumerate().join(', ')}
42 | }
43 |
44 | render() {
45 | const { set1Csv, set2Csv } = this.state
46 |
47 | return (
48 |
49 |
Union of Two Sets Demo
50 |
51 | Enter comma-separated values into each text input to add items to each
52 | set.
53 |
54 |
75 |
76 |
Union of Set 1 and Set 2:
77 | {this.findUnion()}
78 |
79 |
80 | )
81 | }
82 | }
83 |
84 | export const unionOfTwoSetsVisualizer = () =>
85 |
--------------------------------------------------------------------------------
/src/algorithms/set/union/src/union.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Union
3 | *
4 | * Compares two sets and produces a third set that contains all unique values found in either set
5 | *
6 | * Ex. Union of { 1, 2, 3 } and { 3, 4, 5 } is { 1, 2, 3, 4, 5 }
7 | *
8 | * Performance: Linear - O(n + m), where n is the length of set 1 and m is the length of set 2
9 | */
10 |
11 | import { Set } from '../../../../data-structures/set/src/set'
12 |
13 | export const union = (set1, set2) => {
14 | const unionSet = new Set()
15 | set1.enumerate().forEach(val => unionSet.add(val))
16 | set2.enumerate().forEach(val => unionSet.add(val))
17 | return unionSet
18 | }
19 |
--------------------------------------------------------------------------------
/src/algorithms/set/union/src/union.test.js:
--------------------------------------------------------------------------------
1 | import { union } from './union'
2 | import { Set } from '../../../../data-structures/set/src/set'
3 |
4 | describe('union', () => {
5 | it('creates a third set from the two given sets', () => {
6 | const set1 = new Set()
7 | set1.add(42)
8 | set1.add(10)
9 |
10 | const set2 = new Set()
11 | set2.add('a')
12 | set2.add('b')
13 | set2.add('c')
14 |
15 | const set3 = union(set1, set2)
16 |
17 | expect(set3.size()).toBe(5)
18 | expect(set3.enumerate()).toEqual([42, 10, 'a', 'b', 'c'])
19 | })
20 |
21 | it('only adds unique values', () => {
22 | const set1 = new Set()
23 | set1.add(42)
24 | set1.add(10)
25 | set1.add('a')
26 |
27 | const set2 = new Set()
28 | set1.add(42)
29 | set2.add('a')
30 | set2.add('b')
31 | set2.add('c')
32 |
33 | const set3 = union(set1, set2)
34 |
35 | expect(set3.size()).toBe(5)
36 | expect(set3.enumerate()).toEqual([42, 10, 'a', 'b', 'c'])
37 | })
38 |
39 | it('does not modify the original two sets', () => {
40 | const set1 = new Set()
41 | set1.add(42)
42 | set1.add(10)
43 |
44 | const set2 = new Set()
45 | set2.add('a')
46 | set2.add('b')
47 | set2.add('c')
48 |
49 | const set3 = union(set1, set2)
50 |
51 | expect(set3.size()).toBe(5)
52 | expect(set3.enumerate()).toEqual([42, 10, 'a', 'b', 'c'])
53 |
54 | expect(set1.size()).toBe(2)
55 | expect(set1.enumerate()).toEqual([42, 10])
56 |
57 | expect(set2.size()).toBe(3)
58 | expect(set2.enumerate()).toEqual(['a', 'b', 'c'])
59 | })
60 |
61 | it('can handle creating the union of two empty sets', () => {
62 | const set1 = new Set()
63 | const set2 = new Set()
64 | const set3 = union(set1, set2)
65 |
66 | expect(set3.size()).toBe(0)
67 | expect(set3.enumerate()).toEqual([])
68 | })
69 | })
70 |
--------------------------------------------------------------------------------
/src/algorithms/sort/additional-demos/sort-performance-comparison-test.demo.css:
--------------------------------------------------------------------------------
1 | .sortComparisonDemo button,
2 | .sortComparisonDemo label {
3 | margin-bottom: 1rem;
4 | display: inline-block;
5 | }
6 |
7 | .sortComparisonDemo label span {
8 | margin-right: 1rem;
9 | }
10 |
11 | .sortComparisonDemo h3 {
12 | display: inline-block;
13 | margin: 0.5rem 0;
14 | }
15 |
16 | .sortComparisonDemo .arrayData {
17 | margin: 1rem 0;
18 | }
19 |
20 | .sortComparisonDemo .arrayData .elements {
21 | white-space: nowrap;
22 | overflow-x: scroll;
23 | border: 1px solid #071a32;
24 | padding: 0.5rem;
25 | background: #f6f6f6;
26 | border-radius: 0.25rem;
27 | margin-top: 0.55rem;
28 | }
29 |
--------------------------------------------------------------------------------
/src/algorithms/sort/bubble-sort/demo/bubble-sort.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { bubbleSort } from '../src/bubble-sort'
3 | import { SortPerformanceTest } from '../../demo-utils/sort-performance-test.demo'
4 |
5 | export default {
6 | title: 'Algorithms/Sort/Bubble Sort',
7 | }
8 |
9 | export const bubbleSortPerformanceTest = () => (
10 |
11 | )
12 |
--------------------------------------------------------------------------------
/src/algorithms/sort/bubble-sort/src/bubble-sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Bubble Sort
3 | *
4 | * Compares each pair of adjacent items and swaps them if they are in the wrong order
5 | * Continues looping through the array until no more swaps are needed
6 | * Not appropriate for large unsorted data sets
7 | *
8 | * Best case performance: Ω(n) (only one element is out of place, so only one iteration through the array)
9 | * Average case performance: θ(n^2) (array is mostly unsorted, have to do a loop nested within a loop)
10 | * Worst case performance: O(n^2) (array is entirely unsorted, have to do a loop nested within a loop)
11 | *
12 | * Space required: O(n) (because it operates directly on the input array)
13 | */
14 |
15 | export const bubbleSort = (arr, showLogs) => {
16 | const sortedArr = [...arr]
17 | let didSwapSomething = false
18 | let iteration = 0
19 |
20 | /* istanbul ignore next */
21 | showLogs && console.log(`starting array: ${sortedArr.join(' ')}`)
22 |
23 | do {
24 | didSwapSomething = false
25 | iteration++
26 | for (let i = 0; i < sortedArr.length - 1; i++) {
27 | if (sortedArr[i] > sortedArr[i + 1]) {
28 | const firstPosition = sortedArr[i]
29 | sortedArr[i] = sortedArr[i + 1]
30 | sortedArr[i + 1] = firstPosition
31 | didSwapSomething = true
32 | }
33 | }
34 |
35 | /* istanbul ignore next */
36 | showLogs &&
37 | console.log(`iteration ${iteration}: array: ${sortedArr.join(' ')}`)
38 | } while (didSwapSomething)
39 |
40 | return sortedArr
41 | }
42 |
--------------------------------------------------------------------------------
/src/algorithms/sort/bubble-sort/src/bubble-sort.test.js:
--------------------------------------------------------------------------------
1 | import { bubbleSort } from './bubble-sort'
2 |
3 | const showLogs = process.env.SHOW_LOGS === '1'
4 |
5 | describe('bubbleSort', () => {
6 | describe('odd number of elements', () => {
7 | it('correctly sorts an unsorted array', () => {
8 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 7]
9 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
10 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
11 | })
12 |
13 | it('correctly sorts a mostly sorted array', () => {
14 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8]
15 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
16 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
17 | })
18 |
19 | it('correctly sorts an inversely sorted array', () => {
20 | const arrayToSort = [9, 8, 7, 6, 5, 4, 3, 2, 1]
21 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
22 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
23 | })
24 |
25 | it('correctly returns an already sorted array', () => {
26 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9]
27 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
28 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
29 | })
30 |
31 | it('can handle an array with duplicates', () => {
32 | const arrayToSort = [3, 6, 2, 8, 4, 4, 4, 4, 7]
33 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8]
34 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
35 | })
36 |
37 | it('can handle an array with only one item', () => {
38 | const arrayToSort = [1]
39 | const expectedResult = [1]
40 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
41 | })
42 | })
43 |
44 | describe('even number of elements', () => {
45 | it('correctly sorts an unsorted array', () => {
46 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 10, 7]
47 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
48 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
49 | })
50 |
51 | it('correctly sorts a mostly sorted array', () => {
52 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8, 10]
53 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
54 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
55 | })
56 |
57 | it('correctly sorts an inversely sorted array', () => {
58 | const arrayToSort = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
59 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
60 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
61 | })
62 |
63 | it('correctly returns an already sorted array', () => {
64 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
65 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
66 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
67 | })
68 |
69 | it('can handle an array with duplicates', () => {
70 | const arrayToSort = [3, 6, 2, 8, 4, 4, 10, 4, 4, 7]
71 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8, 10]
72 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
73 | })
74 |
75 | it('can handle an empty array', () => {
76 | const arrayToSort = []
77 | const expectedResult = []
78 | expect(bubbleSort(arrayToSort, showLogs)).toEqual(expectedResult)
79 | })
80 | })
81 | })
82 |
--------------------------------------------------------------------------------
/src/algorithms/sort/counting-sort/demo/counting-sort.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { countingSort } from '../src/counting-sort'
3 | import { SortPerformanceTest } from '../../demo-utils/sort-performance-test.demo'
4 |
5 | export default {
6 | title: 'Algorithms/Sort/Counting Sort',
7 | }
8 |
9 | export const countingSortPerformanceTest = () => (
10 |
14 | )
15 |
--------------------------------------------------------------------------------
/src/algorithms/sort/counting-sort/src/counting-sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Counting Sort
3 | *
4 | * Counts the occurrence of each element in the input array and uses that to create a sorted output array
5 | *
6 | * Best case performance: Ω(n + k) (always loops through the input array, count array, and output array)
7 | * Average case performance: θ(n + k) (always loops through the input array, count array, and output array)
8 | * Worst case performance: O(n + k) (always loops through the input array, count array, and output array)
9 | *
10 | * Space required: O(n + k)
11 | */
12 |
13 | export const countingSort = (arr, minValue, maxValue, showLogs) => {
14 | const sortedArr = []
15 | const countArr = []
16 | let min = minValue
17 | let max = maxValue
18 |
19 | /* istanbul ignore next */
20 | showLogs && console.log(`starting array: ${arr.join(' ')}`)
21 |
22 | if (typeof min === 'undefined' || typeof max === 'undefined') {
23 | /* istanbul ignore next */
24 | showLogs && console.log('getting min and max values of the input array')
25 |
26 | for (let i = 0; i < arr.length; i++) {
27 | const currentElement = arr[i]
28 |
29 | if (i === 0) {
30 | min = currentElement
31 | max = currentElement
32 | }
33 |
34 | if (currentElement < min) {
35 | min = currentElement
36 | }
37 |
38 | if (currentElement > max) {
39 | max = currentElement
40 | }
41 | }
42 | }
43 |
44 | /* istanbul ignore next */
45 | showLogs && console.log(`min value: ${min}; max value: ${max}`)
46 |
47 | const countRange = max - min + 1
48 | for (let i = 0; i < countRange; i++) {
49 | countArr.push(0)
50 | }
51 |
52 | /* istanbul ignore next */
53 | showLogs && console.log(`initialized count array: ${countArr.join(' ')}`)
54 |
55 | for (let i = 0; i < arr.length; i++) {
56 | const currentElement = arr[i]
57 | countArr[currentElement - min]++
58 | }
59 |
60 | /* istanbul ignore next */
61 | showLogs && console.log(`populated count array: ${countArr.join(' ')}`)
62 |
63 | for (let i = 0; i < countArr.length; i++) {
64 | while (countArr[i] > 0) {
65 | sortedArr.push(i + min)
66 | countArr[i]--
67 | }
68 | }
69 |
70 | /* istanbul ignore next */
71 | showLogs && console.log(`sorted output array: ${sortedArr.join(' ')}`)
72 |
73 | return sortedArr
74 | }
75 |
--------------------------------------------------------------------------------
/src/algorithms/sort/demo-utils/sort-performance-test.demo.css:
--------------------------------------------------------------------------------
1 | .sortDemo button,
2 | .sortDemo label {
3 | margin-bottom: 1rem;
4 | display: inline-block;
5 | }
6 |
7 | .sortDemo label span {
8 | margin-right: 1rem;
9 | }
10 |
11 | .sortDemo .arrayData .elements {
12 | white-space: nowrap;
13 | overflow-x: scroll;
14 | border: 1px solid #071a32;
15 | padding: 0.5rem;
16 | background: #f6f6f6;
17 | border-radius: 0.25rem;
18 | margin-top: 0.55rem;
19 | }
20 |
--------------------------------------------------------------------------------
/src/algorithms/sort/insertion-sort/demo/insertion-sort.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { insertionSort } from '../src/insertion-sort'
3 | import { SortPerformanceTest } from '../../demo-utils/sort-performance-test.demo'
4 |
5 | export default {
6 | title: 'Algorithms/Sort/Insertion Sort',
7 | }
8 |
9 | export const insertionSortPerformanceTest = () => (
10 |
14 | )
15 |
--------------------------------------------------------------------------------
/src/algorithms/sort/insertion-sort/src/insertion-sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Insertion Sort
3 | *
4 | * Inserts each element into its correct place in the array, one element at a time
5 | * Loops through the array for every element
6 | *
7 | * Everything to the left of the current item is known to be sorted
8 | * Everything to the right of the current item is unsorted
9 | * Not appropriate for large unsorted data sets
10 | *
11 | * Best case performance: Ω(n) (only one element is out of place, so only one iteration through the array)
12 | * Average case performance: 0(n^2) (array is mostly unsorted, have to do a loop nested within a loop)
13 | * Worst case performance: O(n^2) (array is entirely unsorted, have to do a loop nested within a loop)
14 | *
15 | * Space required: O(n) (because it operates directly on the input array)
16 | */
17 |
18 | export const insertionSort = (arr, showLogs) => {
19 | let sortedArr = [...arr]
20 |
21 | /* istanbul ignore next */
22 | showLogs && console.log(`starting array: ${sortedArr.join(' ')}`)
23 |
24 | for (let i = 1; i < sortedArr.length; i++) {
25 | const valueToInsert = sortedArr.splice(i, 1)[0]
26 | let didInsertValue = false
27 | for (let j = i; j >= 1; j--) {
28 | /* istanbul ignore next */
29 | showLogs &&
30 | console.log(` comparing ${valueToInsert} with ${sortedArr[j - 1]}`)
31 |
32 | if (valueToInsert > sortedArr[j - 1]) {
33 | /* istanbul ignore next */
34 | showLogs &&
35 | console.log(
36 | ` inserting ${valueToInsert} after ${sortedArr[j - 1]}`
37 | )
38 | sortedArr = [
39 | ...sortedArr.slice(0, j),
40 | valueToInsert,
41 | ...sortedArr.slice(j),
42 | ]
43 | didInsertValue = true
44 | break
45 | }
46 | }
47 | if (!didInsertValue) {
48 | /* istanbul ignore next */
49 | showLogs && console.log(` inserting ${valueToInsert} at first index`)
50 | sortedArr = [valueToInsert, ...sortedArr]
51 | }
52 |
53 | /* istanbul ignore next */
54 | showLogs && console.log(`iteration ${i + 1}: array: ${sortedArr.join(' ')}`)
55 | }
56 |
57 | return sortedArr
58 | }
59 |
--------------------------------------------------------------------------------
/src/algorithms/sort/insertion-sort/src/insertion-sort.test.js:
--------------------------------------------------------------------------------
1 | import { insertionSort } from './insertion-sort'
2 |
3 | const showLogs = process.env.SHOW_LOGS === '1'
4 |
5 | describe('insertionSort', () => {
6 | describe('odd number of elements', () => {
7 | it('correctly sorts an unsorted array', () => {
8 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 7]
9 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
10 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
11 | })
12 |
13 | it('correctly sorts a mostly sorted array', () => {
14 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8]
15 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
16 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
17 | })
18 |
19 | it('correctly sorts an inversely sorted array', () => {
20 | const arrayToSort = [9, 8, 7, 6, 5, 4, 3, 2, 1]
21 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
22 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
23 | })
24 |
25 | it('correctly returns an already sorted array', () => {
26 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9]
27 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
28 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
29 | })
30 |
31 | it('can handle an array with duplicates', () => {
32 | const arrayToSort = [3, 6, 2, 8, 4, 4, 4, 4, 7]
33 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8]
34 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
35 | })
36 |
37 | it('can handle an array with only one item', () => {
38 | const arrayToSort = [1]
39 | const expectedResult = [1]
40 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
41 | })
42 | })
43 |
44 | describe('even number of elements', () => {
45 | it('correctly sorts an unsorted array', () => {
46 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 10, 7]
47 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
48 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
49 | })
50 |
51 | it('correctly sorts a mostly sorted array', () => {
52 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8, 10]
53 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
54 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
55 | })
56 |
57 | it('correctly sorts an inversely sorted array', () => {
58 | const arrayToSort = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
59 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
60 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
61 | })
62 |
63 | it('correctly returns an already sorted array', () => {
64 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
65 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
66 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
67 | })
68 |
69 | it('can handle an array with duplicates', () => {
70 | const arrayToSort = [3, 6, 2, 8, 4, 4, 10, 4, 4, 7]
71 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8, 10]
72 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
73 | })
74 |
75 | it('can handle an empty array', () => {
76 | const arrayToSort = []
77 | const expectedResult = []
78 | expect(insertionSort(arrayToSort, showLogs)).toEqual(expectedResult)
79 | })
80 | })
81 | })
82 |
--------------------------------------------------------------------------------
/src/algorithms/sort/merge-sort/demo/merge-sort.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { mergeSort } from '../src/merge-sort'
3 | import { SortPerformanceTest } from '../../demo-utils/sort-performance-test.demo'
4 |
5 | export default {
6 | title: 'Algorithms/Sort/Merge Sort',
7 | }
8 |
9 | export const mergeSortPerformanceTest = () => (
10 |
11 | )
12 |
--------------------------------------------------------------------------------
/src/algorithms/sort/merge-sort/src/merge-sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Merge Sort
3 | *
4 | * The array is recursively split in half
5 | * When the array is in groups of 1, it is reconstructed in sort order
6 | * Each reconstructed array is merged with the other half
7 | *
8 | * So if you had an array of 8 items, it’s broken down into two groups of 4,
9 | * then four groups of 2, then eight groups of 1
10 | * Then it’s built back up where you combine the eight groups of 1 into four groups of 2,
11 | * but you sort within each group of 2. Then you combine the four groups of 2 into two
12 | * groups of 4, and sort those groups of 4. Then you combine the two groups of 4 into
13 | * one group of 8, and you sort that group.
14 | *
15 | * Appropriate for large data sets
16 | * Data splitting means that the algorithm can be done in parallel
17 | *
18 | * Best case performance: Ω(n log n)
19 | * Average case performance: 0(n log n)
20 | * Worst case performance: O(n log n)
21 | *
22 | * Space required: O(n) (merge sort can be performed in-place, but it’s often not)
23 | *
24 | * Merge sort is a predictable algorithm since the time complexity is the same for the worst, average, and best case
25 | * If you don’t do it in-place, then you take up extra memory allocations for the temporary arrays
26 | */
27 |
28 | export const mergeSort = (arr, showLogs) => {
29 | /* istanbul ignore next */
30 | showLogs && console.log(`current array: ${arr.join(' ')}`)
31 |
32 | if (arr.length < 2) {
33 | /* istanbul ignore next */
34 | showLogs &&
35 | console.log(' array only has 0 or 1 items, so nothing to sort!')
36 | return arr
37 | }
38 |
39 | // Divide the array into two sub-arrays, right down the middle
40 | /* istanbul ignore next */
41 | showLogs && console.log(' splitting the array down the middle!')
42 | const midpointIndex = Math.floor(arr.length / 2)
43 | const leftSubArray = mergeSort(arr.slice(0, midpointIndex), showLogs)
44 | const rightSubArray = mergeSort(arr.slice(midpointIndex), showLogs)
45 |
46 | return merge(leftSubArray, rightSubArray, showLogs)
47 | }
48 |
49 | const merge = (arr1, arr2, showLogs) => {
50 | /* istanbul ignore next */
51 | showLogs &&
52 | console.log(`merging arrays [${arr1.join(' ')}] and [${arr2.join(' ')}]`)
53 | const result = []
54 | while (arr1.length > 0 && arr2.length > 0) {
55 | // The two sub-arrays are always sorted, so in order to combine the two sub-arrays,
56 | // we need to pick off the first value from whichever array currently has
57 | // a lower value as its first element
58 | result.push(arr1[0] < arr2[0] ? arr1.shift() : arr2.shift())
59 | /* istanbul ignore next */
60 | showLogs &&
61 | console.log(
62 | ` current merge result from the two sub-arrays: ${result.join(' ')}`
63 | )
64 | }
65 |
66 | // The while loop stops once one sub-array no longer has any elements,
67 | // so this combines the result array with whichever sub-array still
68 | // has elements in it
69 | const finalResult = result.concat(arr1.length ? arr1 : arr2)
70 | /* istanbul ignore next */
71 | showLogs &&
72 | console.log(
73 | ` final merge result from the two sub-arrays: ${finalResult.join(' ')}`
74 | )
75 |
76 | return finalResult
77 | }
78 |
--------------------------------------------------------------------------------
/src/algorithms/sort/merge-sort/src/merge-sort.test.js:
--------------------------------------------------------------------------------
1 | import { mergeSort } from './merge-sort'
2 |
3 | const showLogs = process.env.SHOW_LOGS === '1'
4 |
5 | describe('mergeSort', () => {
6 | describe('odd number of elements', () => {
7 | it('correctly sorts an unsorted array', () => {
8 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 7]
9 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
10 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
11 | })
12 |
13 | it('correctly sorts a mostly sorted array', () => {
14 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8]
15 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
16 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
17 | })
18 |
19 | it('correctly sorts an inversely sorted array', () => {
20 | const arrayToSort = [9, 8, 7, 6, 5, 4, 3, 2, 1]
21 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
22 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
23 | })
24 |
25 | it('correctly returns an already sorted array', () => {
26 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9]
27 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
28 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
29 | })
30 |
31 | it('can handle an array with duplicates', () => {
32 | const arrayToSort = [3, 6, 2, 8, 4, 4, 4, 4, 7]
33 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8]
34 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
35 | })
36 |
37 | it('can handle an array with only one item', () => {
38 | const arrayToSort = [1]
39 | const expectedResult = [1]
40 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
41 | })
42 | })
43 |
44 | describe('even number of elements', () => {
45 | it('correctly sorts an unsorted array', () => {
46 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 10, 7]
47 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
48 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
49 | })
50 |
51 | it('correctly sorts a mostly sorted array', () => {
52 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8, 10]
53 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
54 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
55 | })
56 |
57 | it('correctly sorts an inversely sorted array', () => {
58 | const arrayToSort = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
59 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
60 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
61 | })
62 |
63 | it('correctly returns an already sorted array', () => {
64 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
65 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
66 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
67 | })
68 |
69 | it('can handle an array with duplicates', () => {
70 | const arrayToSort = [3, 6, 2, 8, 4, 4, 10, 4, 4, 7]
71 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8, 10]
72 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
73 | })
74 |
75 | it('can handle an empty array', () => {
76 | const arrayToSort = []
77 | const expectedResult = []
78 | expect(mergeSort(arrayToSort, showLogs)).toEqual(expectedResult)
79 | })
80 | })
81 | })
82 |
--------------------------------------------------------------------------------
/src/algorithms/sort/quick-sort/demo/quick-sort.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { quickSort } from '../src/quick-sort'
3 | import { SortPerformanceTest } from '../../demo-utils/sort-performance-test.demo'
4 |
5 | export default {
6 | title: 'Algorithms/Sort/Quick Sort',
7 | }
8 |
9 | export const quickSortPerformanceTest = () => (
10 |
11 | )
12 |
--------------------------------------------------------------------------------
/src/algorithms/sort/quick-sort/src/quick-sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Quick Sort
3 | *
4 | * Pick a pivot value and partition the array
5 | * Put all values smaller than the pivot to the left and all values larger
6 | * than the pivot to the right
7 | * Then continue to perform the pivot and partition algorithm on the left
8 | * and right partitions, and repeat until it’s all sorted
9 | *
10 | * Not appropriate for large inverse-sorted data sets
11 | * You are doing recursion, so be aware that it puts a new function call
12 | * on the stack the deeper you go
13 | *
14 | * Best case performance: Ω(n log n)
15 | * Average case performance: 0(n log n)
16 | * Worst case performance: O(n^2)
17 | *
18 | * Space required: O(n) (because it operates directly on the input array)
19 | */
20 |
21 | export const quickSort = (arr, left = 0, right = arr.length - 1, showLogs) => {
22 | /* istanbul ignore next */
23 | showLogs && console.log(`current array: ${arr.join(' ')}`)
24 |
25 | if (arr.length < 2) {
26 | /* istanbul ignore next */
27 | showLogs &&
28 | console.log(' array only has 0 or 1 items, so nothing to sort!')
29 | return arr
30 | }
31 |
32 | const pivotIndex = partition(arr, left, right, showLogs) // index returned from partition
33 |
34 | if (left < pivotIndex - 1) {
35 | // more elements on the left side of the pivot
36 | quickSort(arr, left, pivotIndex - 1, showLogs)
37 | }
38 |
39 | if (pivotIndex < right) {
40 | // more elements on the right side of the pivot
41 | quickSort(arr, pivotIndex, right, showLogs)
42 | }
43 |
44 | return arr
45 | }
46 |
47 | // swaps the position of two elements in an array
48 | const swap = (arr, leftIndex, rightIndex, showLogs) => {
49 | /* istanbul ignore next */
50 | showLogs &&
51 | console.log(
52 | ` swapping elements with values ${arr[leftIndex]} and ${arr[rightIndex]}`
53 | )
54 |
55 | const temp = arr[leftIndex]
56 | arr[leftIndex] = arr[rightIndex]
57 | arr[rightIndex] = temp
58 | }
59 |
60 | const partition = (arr, leftIndex, rightIndex, showLogs) => {
61 | const pivotIndex = Math.floor((rightIndex + leftIndex) / 2)
62 | const pivotElement = arr[pivotIndex]
63 |
64 | /* istanbul ignore next */
65 | showLogs && console.log(` pivot element chosen is ${pivotElement}`)
66 |
67 | let currentLeftIndex = leftIndex
68 | let currentRightIndex = rightIndex
69 |
70 | /* istanbul ignore next */
71 | showLogs &&
72 | console.log(
73 | ` current left element: ${arr[currentLeftIndex]}, current right element: ${arr[currentRightIndex]}`
74 | )
75 |
76 | while (currentLeftIndex <= currentRightIndex) {
77 | while (arr[currentLeftIndex] < pivotElement) {
78 | /* istanbul ignore next */
79 | showLogs &&
80 | console.log(
81 | ` left element ${
82 | arr[currentLeftIndex]
83 | } is less than ${pivotElement}, moving right by one element to ${
84 | arr[currentLeftIndex + 1]
85 | }`
86 | )
87 |
88 | currentLeftIndex++
89 | }
90 |
91 | /* istanbul ignore next */
92 | showLogs &&
93 | console.log(
94 | ` left element ${arr[currentLeftIndex]} is greater than or equal to ${pivotElement}, so ${arr[currentLeftIndex]} is eligible to be swapped`
95 | )
96 |
97 | while (arr[currentRightIndex] > pivotElement) {
98 | /* istanbul ignore next */
99 | showLogs &&
100 | console.log(
101 | ` right element ${
102 | arr[currentRightIndex]
103 | } is greater than ${pivotElement}, moving left by one element to ${
104 | arr[currentRightIndex - 1]
105 | }`
106 | )
107 |
108 | currentRightIndex--
109 | }
110 |
111 | /* istanbul ignore next */
112 | showLogs &&
113 | console.log(
114 | ` right element ${arr[currentRightIndex]} is less than or equal to ${pivotElement}, so ${arr[currentRightIndex]} is eligible to be swapped`
115 | )
116 |
117 | if (currentLeftIndex <= currentRightIndex) {
118 | /* istanbul ignore next */
119 | showLogs &&
120 | console.log(
121 | ` left element ${arr[currentLeftIndex]} is less than or equal to right element ${arr[currentRightIndex]}, so we can swap these two elements`
122 | )
123 |
124 | swap(arr, currentLeftIndex, currentRightIndex, showLogs)
125 | currentLeftIndex++
126 | currentRightIndex--
127 |
128 | /* istanbul ignore next */
129 | showLogs && console.log(` resulting array: ${arr.join(' ')}`)
130 | } else {
131 | /* istanbul ignore next */
132 | showLogs &&
133 | console.log(
134 | ` left element ${arr[currentLeftIndex]} is NOT less than or equal to right element ${arr[currentRightIndex]}, so we will NOT swap these two elements`
135 | )
136 | }
137 | }
138 |
139 | return currentLeftIndex
140 | }
141 |
--------------------------------------------------------------------------------
/src/algorithms/sort/quick-sort/src/quick-sort.test.js:
--------------------------------------------------------------------------------
1 | import { quickSort } from './quick-sort'
2 |
3 | const showLogs = process.env.SHOW_LOGS === '1'
4 |
5 | describe('quickSort', () => {
6 | describe('odd number of elements', () => {
7 | it('correctly sorts an unsorted array', () => {
8 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 7]
9 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
10 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
11 | expectedResult
12 | )
13 | })
14 |
15 | it('correctly sorts a mostly sorted array', () => {
16 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8]
17 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
18 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
19 | expectedResult
20 | )
21 | })
22 |
23 | it('correctly sorts an inversely sorted array', () => {
24 | const arrayToSort = [9, 8, 7, 6, 5, 4, 3, 2, 1]
25 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
26 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
27 | expectedResult
28 | )
29 | })
30 |
31 | it('correctly returns an already sorted array', () => {
32 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9]
33 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
34 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
35 | expectedResult
36 | )
37 | })
38 |
39 | it('can handle an array with duplicates', () => {
40 | const arrayToSort = [3, 6, 2, 8, 4, 4, 4, 4, 7]
41 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8]
42 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
43 | expectedResult
44 | )
45 | })
46 |
47 | it('can handle an array with only one item', () => {
48 | const arrayToSort = [1]
49 | const expectedResult = [1]
50 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
51 | expectedResult
52 | )
53 | })
54 | })
55 |
56 | describe('even number of elements', () => {
57 | it('correctly sorts an unsorted array', () => {
58 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 10, 7]
59 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
60 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
61 | expectedResult
62 | )
63 | })
64 |
65 | it('correctly sorts a mostly sorted array', () => {
66 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8, 10]
67 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
68 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
69 | expectedResult
70 | )
71 | })
72 |
73 | it('correctly sorts an inversely sorted array', () => {
74 | const arrayToSort = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
75 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
76 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
77 | expectedResult
78 | )
79 | })
80 |
81 | it('correctly returns an already sorted array', () => {
82 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
83 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
84 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
85 | expectedResult
86 | )
87 | })
88 |
89 | it('can handle an array with duplicates', () => {
90 | const arrayToSort = [3, 6, 2, 8, 4, 4, 10, 4, 4, 7]
91 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8, 10]
92 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
93 | expectedResult
94 | )
95 | })
96 |
97 | it('can handle an empty array', () => {
98 | const arrayToSort = []
99 | const expectedResult = []
100 | expect(quickSort(arrayToSort, undefined, undefined, showLogs)).toEqual(
101 | expectedResult
102 | )
103 | })
104 | })
105 | })
106 |
--------------------------------------------------------------------------------
/src/algorithms/sort/selection-sort/demo/selection-sort.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { selectionSort } from '../src/selection-sort'
3 | import { SortPerformanceTest } from '../../demo-utils/sort-performance-test.demo'
4 |
5 | export default {
6 | title: 'Algorithms/Sort/Selection Sort',
7 | }
8 |
9 | export const selectionSortPerformanceTest = () => (
10 |
14 | )
15 |
--------------------------------------------------------------------------------
/src/algorithms/sort/selection-sort/src/selection-sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Selection Sort
3 | *
4 | * Finds the lowest value in the remaining unsorted items in the array
5 | * and swaps it into the earliest unsorted position
6 | * Loops through the array for every element
7 | *
8 | * The bubble sort iterates through the entire array for each pass,
9 | * but the selection sort only has to iterate through the remaining unsorted
10 | * items for each pass, making it a little more efficient
11 | * Not appropriate for large unsorted data sets
12 | *
13 | * Best case performance: Ω(n^2) (always loops through the entire array)
14 | * Average case performance: 0(n^2) (always loops through the entire array)
15 | * Worst case performance: O(n^2) (always loops through the entire array)
16 | *
17 | * Space required: O(n) (because it operates directly on the input array)
18 | */
19 |
20 | export const selectionSort = (arr, showLogs) => {
21 | const sortedArr = [...arr]
22 |
23 | /* istanbul ignore next */
24 | showLogs && console.log(`starting array: ${sortedArr.join(' ')}`)
25 |
26 | for (let i = 0; i < sortedArr.length - 1; i++) {
27 | let minValueIndex = i
28 |
29 | for (let j = i; j < sortedArr.length; j++) {
30 | if (sortedArr[j] < sortedArr[minValueIndex]) {
31 | minValueIndex = j
32 | }
33 | }
34 |
35 | const originalStartingValue = sortedArr[i]
36 | sortedArr[i] = sortedArr[minValueIndex]
37 | sortedArr[minValueIndex] = originalStartingValue
38 |
39 | /* istanbul ignore next */
40 | showLogs && console.log(`iteration ${i + 1}: array: ${sortedArr.join(' ')}`)
41 | }
42 |
43 | return sortedArr
44 | }
45 |
--------------------------------------------------------------------------------
/src/algorithms/sort/selection-sort/src/selection-sort.test.js:
--------------------------------------------------------------------------------
1 | import { selectionSort } from './selection-sort'
2 |
3 | const showLogs = process.env.SHOW_LOGS === '1'
4 |
5 | describe('selectionSort', () => {
6 | describe('odd number of elements', () => {
7 | it('correctly sorts an unsorted array', () => {
8 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 7]
9 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
10 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
11 | })
12 |
13 | it('correctly sorts a mostly sorted array', () => {
14 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8]
15 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
16 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
17 | })
18 |
19 | it('correctly sorts an inversely sorted array', () => {
20 | const arrayToSort = [9, 8, 7, 6, 5, 4, 3, 2, 1]
21 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
22 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
23 | })
24 |
25 | it('correctly returns an already sorted array', () => {
26 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9]
27 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9]
28 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
29 | })
30 |
31 | it('can handle an array with duplicates', () => {
32 | const arrayToSort = [3, 6, 2, 8, 4, 4, 4, 4, 7]
33 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8]
34 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
35 | })
36 |
37 | it('can handle an array with only one item', () => {
38 | const arrayToSort = [1]
39 | const expectedResult = [1]
40 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
41 | })
42 | })
43 |
44 | describe('even number of elements', () => {
45 | it('correctly sorts an unsorted array', () => {
46 | const arrayToSort = [3, 6, 2, 8, 9, 1, 4, 5, 10, 7]
47 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
48 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
49 | })
50 |
51 | it('correctly sorts a mostly sorted array', () => {
52 | const arrayToSort = [1, 2, 9, 3, 4, 5, 6, 7, 8, 10]
53 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
54 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
55 | })
56 |
57 | it('correctly sorts an inversely sorted array', () => {
58 | const arrayToSort = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
59 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
60 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
61 | })
62 |
63 | it('correctly returns an already sorted array', () => {
64 | const arrayToSort = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
65 | const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
66 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
67 | })
68 |
69 | it('can handle an array with duplicates', () => {
70 | const arrayToSort = [3, 6, 2, 8, 4, 4, 10, 4, 4, 7]
71 | const expectedResult = [2, 3, 4, 4, 4, 4, 6, 7, 8, 10]
72 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
73 | })
74 |
75 | it('can handle an empty array', () => {
76 | const arrayToSort = []
77 | const expectedResult = []
78 | expect(selectionSort(arrayToSort, showLogs)).toEqual(expectedResult)
79 | })
80 | })
81 | })
82 |
--------------------------------------------------------------------------------
/src/data-structures/array/demo/array.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default {
4 | title: 'Data Structures/Array',
5 | }
6 |
7 | export const readTheDocs = () => (
8 |
9 |
Arrays are already implemented in JavaScript!
10 |
11 | Go read the docs here:{' '}
12 |
17 | MDN - Array
18 |
19 |
20 |
21 | )
22 |
--------------------------------------------------------------------------------
/src/data-structures/array/src/array.js:
--------------------------------------------------------------------------------
1 | // Nothing to do here! Arrays are already implemented in JavaScript
2 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
3 |
--------------------------------------------------------------------------------
/src/data-structures/binary-search-tree/demo/binary-search-tree.demo.css:
--------------------------------------------------------------------------------
1 | .binarySearchTreeDemo .flexContainer {
2 | display: flex;
3 | flex-direction: row;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .binarySearchTreeDemo .flexContainer .leftColumn {
8 | margin-right: 2rem;
9 | }
10 |
11 | .binarySearchTreeDemo .flexContainer .rightColumn {
12 | flex-shrink: 0;
13 | }
14 |
15 | .binarySearchTreeDemo .binarySearchTreeContainer {
16 | width: auto;
17 | margin-bottom: 0.25rem;
18 | }
19 |
20 | .binarySearchTreeDemo h2 {
21 | margin-top: 0;
22 | }
23 |
24 | .binarySearchTreeDemo .node {
25 | padding: 1rem;
26 | margin-bottom: 0.25rem;
27 | border: 1px solid #071a32;
28 | border-radius: 0.25rem;
29 | background: #f2f2f2;
30 | color: #071a32;
31 | text-align: center;
32 | word-break: break-word;
33 | word-wrap: break-word;
34 | min-width: 0;
35 | }
36 |
37 | .binarySearchTreeDemo button,
38 | .binarySearchTreeDemo label {
39 | margin-bottom: 0.5rem;
40 | display: inline-block;
41 | }
42 |
43 | .binarySearchTreeDemo label span {
44 | margin-right: 1rem;
45 | }
46 |
47 | .binarySearchTreeDemo fieldset {
48 | margin-bottom: 0.5rem;
49 | }
50 |
--------------------------------------------------------------------------------
/src/data-structures/binary-search-tree/src/node.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Node - Contains a value and a pointer to the left node and to the right node
3 | *
4 | * Meant to be used with a binary search tree
5 | *
6 | * Methods and properties:
7 | *
8 | * - val: any value
9 | * - left: pointer to the left node (or null)
10 | * - right: pointer to the right node (or null)
11 | */
12 | export class Node {
13 | constructor(val) {
14 | this.val = val
15 | this.left = null
16 | this.right = null
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/data-structures/binary-search-tree/src/node.test.js:
--------------------------------------------------------------------------------
1 | import { Node } from './node'
2 |
3 | describe('Node', () => {
4 | it('can be instantiated with the `new` keyword', () => {
5 | expect(() => new Node()).not.toThrow()
6 | })
7 |
8 | it('can return its value', () => {
9 | const node1 = new Node(42)
10 | expect(node1.val).toBe(42)
11 | })
12 |
13 | it('can return its left pointer', () => {
14 | const node1 = new Node(42)
15 | expect(node1.left).toBe(null)
16 | })
17 |
18 | it('can return its right pointer', () => {
19 | const node1 = new Node(42)
20 | expect(node1.right).toBe(null)
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/src/data-structures/directed-graph/src/directed-graph.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Directed Graph - consists of nodes (vertices) connected by edges. Connections go only one direction.
3 | *
4 | * Methods and properties:
5 | *
6 | * - addNode: Constant O(1)
7 | * - addEdge: Constant O(1)
8 | * - hadNode: Constant O(1)
9 | * - getNodeNeighbors: Constant O(1)
10 | * - printGraph: O(V + E)
11 | * - breadthFirstSearch: O(V + E)
12 | * - depthFirstSearch: O(V + E)
13 | */
14 |
15 | export class DirectedGraph {
16 | constructor() {
17 | this.nodes = new Map()
18 | }
19 |
20 | addNode(node) {
21 | this.nodes.set(node, [])
22 | }
23 |
24 | addEdge(node1, node2) {
25 | if (this.nodes.has(node1) && this.nodes.has(node2)) {
26 | this.nodes.get(node1).push(node2)
27 | } else {
28 | throw new Error('One or both nodes do not exist in the graph.')
29 | }
30 | }
31 |
32 | hasNode(node) {
33 | return this.nodes.has(node)
34 | }
35 |
36 | getNodeNeighbors(node) {
37 | if (this.nodes.has(node)) {
38 | return this.nodes.get(node)
39 | }
40 |
41 | throw new Error('Node does not exist in the graph.')
42 | }
43 |
44 | printGraph() {
45 | if (this.nodes.size === 0) {
46 | console.log('')
47 | return
48 | }
49 |
50 | let printedMessage = ''
51 | for (const [node, neighbors] of this.nodes.entries()) {
52 | printedMessage += `${node} ->`
53 |
54 | neighbors.forEach(neighborNode => {
55 | printedMessage += ` ${neighborNode}`
56 | })
57 |
58 | printedMessage += '\n'
59 | }
60 |
61 | console.log(printedMessage)
62 | }
63 |
64 | breadthFirstSearch(startingNode, searchingForNode, callback, showLogs) {
65 | const visitedNodes = {}
66 | const nodesToVisitQueue = []
67 |
68 | nodesToVisitQueue.push(startingNode)
69 |
70 | while (nodesToVisitQueue.length > 0) {
71 | const currentNode = nodesToVisitQueue.shift()
72 |
73 | if (!visitedNodes[currentNode]) {
74 | visitedNodes[currentNode] = true
75 | callback(currentNode)
76 |
77 | if (currentNode === searchingForNode) {
78 | break
79 | }
80 |
81 | const currentNodeNeighbors = this.nodes.get(currentNode)
82 |
83 | /* istanbul ignore next */
84 | showLogs &&
85 | console.log(
86 | 'currentNode:',
87 | currentNode,
88 | 'currentNodeNeighbors:',
89 | currentNodeNeighbors.join(', ')
90 | )
91 |
92 | currentNodeNeighbors.forEach(neighborNode => {
93 | nodesToVisitQueue.push(neighborNode)
94 | })
95 | }
96 | }
97 | }
98 |
99 | depthFirstSearch(startingNode, searchingForNode, callback, showLogs) {
100 | const visitedNodes = {}
101 | const nodesToVisitStack = []
102 |
103 | nodesToVisitStack.unshift(startingNode)
104 |
105 | while (nodesToVisitStack.length > 0) {
106 | const currentNode = nodesToVisitStack.shift()
107 |
108 | if (!visitedNodes[currentNode]) {
109 | visitedNodes[currentNode] = true
110 | callback(currentNode)
111 |
112 | if (currentNode === searchingForNode) {
113 | break
114 | }
115 |
116 | const currentNodeNeighbors = this.nodes.get(currentNode)
117 |
118 | /* istanbul ignore next */
119 | showLogs &&
120 | console.log(
121 | 'currentNode:',
122 | currentNode,
123 | 'currentNodeNeighbors:',
124 | currentNodeNeighbors.join(', ')
125 | )
126 |
127 | currentNodeNeighbors.forEach(neighborNode => {
128 | nodesToVisitStack.unshift(neighborNode)
129 | })
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/data-structures/doubly-linked-list/demo/doubly-linked-list.demo.css:
--------------------------------------------------------------------------------
1 | .doublyLinkedListDemo .flexContainer {
2 | display: flex;
3 | flex-direction: row;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .doublyLinkedListDemo .flexContainer .leftColumn {
8 | margin-right: 2rem;
9 | }
10 |
11 | .doublyLinkedListDemo .flexContainer .rightColumn {
12 | flex-shrink: 0;
13 | }
14 |
15 | .doublyLinkedListDemo .doublyLinkedListContainer {
16 | width: 17rem;
17 | border: 1px solid #071a32;
18 | border-radius: 0.25rem;
19 | padding: 1rem;
20 | margin-bottom: 0.25rem;
21 | }
22 |
23 | .doublyLinkedListDemo h2 {
24 | margin-top: 0;
25 | }
26 |
27 | .doublyLinkedListDemo .node {
28 | padding: 1rem;
29 | margin-bottom: 0.25rem;
30 | border: 1px solid #071a32;
31 | border-radius: 0.25rem;
32 | background: #f2f2f2;
33 | color: #071a32;
34 | text-align: center;
35 | word-break: break-word;
36 | word-wrap: break-word;
37 | min-width: 0;
38 | }
39 |
40 | .doublyLinkedListDemo button,
41 | .doublyLinkedListDemo label {
42 | margin-bottom: 0.5rem;
43 | display: inline-block;
44 | }
45 |
46 | .doublyLinkedListDemo label span {
47 | margin-right: 1rem;
48 | }
49 |
50 | .doublyLinkedListDemo fieldset {
51 | margin-bottom: 0.5rem;
52 | }
53 |
--------------------------------------------------------------------------------
/src/data-structures/doubly-linked-list/demo/node.demo.css:
--------------------------------------------------------------------------------
1 | .nodeDemo .nodeContainer {
2 | width: 15rem;
3 | }
4 |
5 | .nodeDemo .node {
6 | padding: 1rem;
7 | margin-bottom: 0.25rem;
8 | border: 1px solid #071a32;
9 | border-radius: 0.25rem;
10 | background: #f2f2f2;
11 | color: #071a32;
12 | text-align: left;
13 | word-break: break-word;
14 | word-wrap: break-word;
15 | min-width: 0;
16 | }
17 |
18 | .nodeDemo .node pre {
19 | white-space: pre-wrap;
20 | text-align: left;
21 | }
22 |
23 | .nodeDemo button,
24 | .nodeDemo label {
25 | margin-bottom: 0.5rem;
26 | display: inline-block;
27 | }
28 |
29 | .nodeDemo label span {
30 | margin-right: 1rem;
31 | }
32 |
--------------------------------------------------------------------------------
/src/data-structures/doubly-linked-list/src/node.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Node - Contains a value and a pointer to the next node and to the previous node
3 | *
4 | * Meant to be used with a doubly linked list
5 | *
6 | * Methods and properties:
7 | *
8 | * - val: any value
9 | * - next: pointer to the next node (or null)
10 | * - prev: pointer to the previous node (or null)
11 | */
12 | export class Node {
13 | constructor(val, next = null, prev = null) {
14 | this.val = val
15 | this.next = next
16 | this.prev = prev
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/data-structures/doubly-linked-list/src/node.test.js:
--------------------------------------------------------------------------------
1 | import { Node } from './node'
2 |
3 | describe('Node', () => {
4 | it('can be instantiated with the `new` keyword', () => {
5 | expect(() => new Node()).not.toThrow()
6 | })
7 |
8 | it('can return its value', () => {
9 | const node1 = new Node(42)
10 | expect(node1.val).toBe(42)
11 | })
12 |
13 | it('can return its next pointer when next is null', () => {
14 | const node1 = new Node(42)
15 | expect(node1.next).toBe(null)
16 | })
17 |
18 | it('can return its next pointer when next is not null', () => {
19 | const node1 = new Node(42)
20 | const node2 = new Node(100, node1)
21 | expect(node2.next).toBe(node1)
22 | })
23 |
24 | it('can return its prev pointer when prev is null', () => {
25 | const node1 = new Node(42)
26 | expect(node1.prev).toBe(null)
27 | })
28 |
29 | it('can return its prev pointer when prev is not null', () => {
30 | const node1 = new Node(42)
31 | const node2 = new Node(100, null, node1)
32 | expect(node2.prev).toBe(node1)
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/src/data-structures/hash-table/demo/hash-table.demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default {
4 | title: 'Data Structures/Hash Table',
5 | }
6 |
7 | export const readTheDocs = () => (
8 |
9 |
Objects in JavaScript are hash tables!
10 |
11 | Go read the docs here:{' '}
12 |
17 | MDN - Object
18 |
19 |
20 |
21 | )
22 |
--------------------------------------------------------------------------------
/src/data-structures/hash-table/src/hash-table.js:
--------------------------------------------------------------------------------
1 | // Nothing to do here! Objects in JavaScript are hash tables!
2 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
3 |
--------------------------------------------------------------------------------
/src/data-structures/linked-list/demo/linked-list.demo.css:
--------------------------------------------------------------------------------
1 | .linkedListDemo .flexContainer {
2 | display: flex;
3 | flex-direction: row;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .linkedListDemo .flexContainer .leftColumn {
8 | margin-right: 2rem;
9 | }
10 |
11 | .linkedListDemo .flexContainer .rightColumn {
12 | flex-shrink: 0;
13 | }
14 |
15 | .linkedListDemo .linkedListContainer {
16 | width: 17rem;
17 | border: 1px solid #071a32;
18 | border-radius: 0.25rem;
19 | padding: 1rem;
20 | margin-bottom: 0.25rem;
21 | }
22 |
23 | .linkedListDemo h2 {
24 | margin-top: 0;
25 | }
26 |
27 | .linkedListDemo .node {
28 | padding: 1rem;
29 | margin-bottom: 0.25rem;
30 | border: 1px solid #071a32;
31 | border-radius: 0.25rem;
32 | background: #f2f2f2;
33 | color: #071a32;
34 | text-align: center;
35 | word-break: break-word;
36 | word-wrap: break-word;
37 | min-width: 0;
38 | }
39 |
40 | .linkedListDemo button,
41 | .linkedListDemo label {
42 | margin-bottom: 0.5rem;
43 | display: inline-block;
44 | }
45 |
46 | .linkedListDemo label span {
47 | margin-right: 1rem;
48 | }
49 |
50 | .linkedListDemo fieldset {
51 | margin-bottom: 0.5rem;
52 | }
53 |
--------------------------------------------------------------------------------
/src/data-structures/linked-list/demo/node.demo.css:
--------------------------------------------------------------------------------
1 | .nodeDemo .nodeContainer {
2 | width: 15rem;
3 | }
4 |
5 | .nodeDemo .node {
6 | padding: 1rem;
7 | margin-bottom: 0.25rem;
8 | border: 1px solid #071a32;
9 | border-radius: 0.25rem;
10 | background: #f2f2f2;
11 | color: #071a32;
12 | text-align: left;
13 | word-break: break-word;
14 | word-wrap: break-word;
15 | min-width: 0;
16 | }
17 |
18 | .nodeDemo .node pre {
19 | white-space: pre-wrap;
20 | text-align: left;
21 | }
22 |
23 | .nodeDemo button,
24 | .nodeDemo label {
25 | margin-bottom: 0.5rem;
26 | display: inline-block;
27 | }
28 |
29 | .nodeDemo label span {
30 | margin-right: 1rem;
31 | }
32 |
--------------------------------------------------------------------------------
/src/data-structures/linked-list/demo/node.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component, createRef } from 'react'
2 | import { Node } from '../src/node'
3 | import './node.demo.css'
4 |
5 | const NodeItem = ({ node }) => {
6 | return (
7 |
8 |
9 | Value: {node.val}
10 |
11 |
12 |
Next:{' '}
13 |
{JSON.stringify(node.next, undefined, 2) || 'null'}
14 |
15 |
16 | )
17 | }
18 |
19 | export class NodeVisualizer extends Component {
20 | state = {
21 | nodes: [],
22 | item: '',
23 | next: '',
24 | }
25 |
26 | textInputRef = createRef()
27 |
28 | handleItemChange = e => {
29 | this.setState({ item: e.target.value })
30 | }
31 |
32 | handleNextChange = e => {
33 | this.setState({ next: e.target.value })
34 | }
35 |
36 | addItem = e => {
37 | e.preventDefault()
38 | const { item, next } = this.state
39 | if (item) {
40 | const node = new Node(item, JSON.parse(next || null))
41 | this.setState(
42 | prevState => ({
43 | nodes: [...prevState.nodes, node],
44 | item: '',
45 | next: '',
46 | }),
47 | () => this.textInputRef.current && this.textInputRef.current.focus()
48 | )
49 | }
50 | }
51 |
52 | removeItem = () => {
53 | this.setState(prevState => ({ nodes: prevState.nodes.slice(1) }))
54 | }
55 |
56 | clearNodes = () => {
57 | this.setState({ nodes: [] })
58 | }
59 |
60 | render() {
61 | const { nodes, item, next } = this.state
62 |
63 | return (
64 |
65 |
Node Demo
66 |
67 | NOTE: This demo simply shows a collection of nodes that have pointers
68 | to their next node. This is not an actual linked list.
69 |
70 |
119 |
120 |
Node List Contents:
121 |
122 | {nodes.map((node, index) => {
123 | return
124 | })}
125 |
126 |
127 |
128 | )
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/data-structures/linked-list/src/node.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Node - Contains a value and a pointer to the next node
3 | *
4 | * Meant to be used with a singly linked list
5 | *
6 | * Methods and properties:
7 | *
8 | * - val: any value
9 | * - next: pointer to the next node (or null)
10 | */
11 |
12 | export class Node {
13 | constructor(val, next = null) {
14 | this.val = val
15 | this.next = next
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/data-structures/linked-list/src/node.test.js:
--------------------------------------------------------------------------------
1 | import { Node } from './node'
2 |
3 | describe('Node', () => {
4 | it('can be instantiated with the `new` keyword', () => {
5 | expect(() => new Node()).not.toThrow()
6 | })
7 |
8 | it('can return its value', () => {
9 | const node1 = new Node(42)
10 | expect(node1.val).toBe(42)
11 | })
12 |
13 | it('can return its next pointer when next is null', () => {
14 | const node1 = new Node(42)
15 | expect(node1.next).toBe(null)
16 | })
17 |
18 | it('can return its next pointer when next is not null', () => {
19 | const node1 = new Node(42)
20 | const node2 = new Node(100, node1)
21 | expect(node2.next).toBe(node1)
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/src/data-structures/priority-queue/demo/priority-queue.demo.css:
--------------------------------------------------------------------------------
1 | .priorityQueueDemo .flexContainer {
2 | display: flex;
3 | flex-direction: row;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .priorityQueueDemo .flexContainer .leftColumn {
8 | margin-right: 2rem;
9 | }
10 |
11 | .priorityQueueDemo .flexContainer .rightColumn {
12 | flex-shrink: 0;
13 | }
14 |
15 | .priorityQueueDemo .priorityQueueContainer {
16 | width: 17rem;
17 | border: 1px solid #071a32;
18 | border-radius: 0.25rem;
19 | padding: 1rem;
20 | margin-bottom: 0.25rem;
21 | }
22 |
23 | .priorityQueueDemo h2 {
24 | margin-top: 0;
25 | }
26 |
27 | .priorityQueueDemo .item {
28 | padding: 1rem;
29 | margin-bottom: 0.25rem;
30 | border: 1px solid #071a32;
31 | border-radius: 0.25rem;
32 | background: #f2f2f2;
33 | color: #071a32;
34 | text-align: center;
35 | word-break: break-word;
36 | word-wrap: break-word;
37 | min-width: 0;
38 | }
39 |
40 | .priorityQueueDemo .item.alt {
41 | background: #ccc;
42 | }
43 |
44 | .priorityQueueDemo button,
45 | .priorityQueueDemo label {
46 | margin-bottom: 0.5rem;
47 | display: inline-block;
48 | }
49 |
50 | .priorityQueueDemo label span {
51 | margin-right: 1rem;
52 | }
53 |
54 | .priorityQueueDemo .frontOfPriorityQueue {
55 | border-bottom: 1px solid #071a32;
56 | font-weight: bold;
57 | }
58 |
59 | .priorityQueueDemo .endOfPriorityQueue {
60 | border-top: 1px solid #071a32;
61 | font-weight: bold;
62 | }
63 |
--------------------------------------------------------------------------------
/src/data-structures/priority-queue/demo/priority-queue.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component, createRef } from 'react'
2 | import { PriorityQueue } from '../src/priority-queue'
3 | import './priority-queue.demo.css'
4 |
5 | export default {
6 | title: 'Data Structures/Priority Queue',
7 | }
8 |
9 | const Item = ({ item, className }) => {
10 | return (
11 |
12 |
13 | Value: {item.value}
14 |
15 |
16 | Priority: {item.priority}
17 |
18 |
19 | )
20 | }
21 |
22 | const generateOddOrEvenClassName = index =>
23 | index % 2 === 0 ? 'item' : 'item alt'
24 |
25 | class PriorityQueueVisualizer extends Component {
26 | state = {
27 | priorityQueue: new PriorityQueue(),
28 | item: '',
29 | priority: 1,
30 | }
31 |
32 | textInputRef = createRef()
33 |
34 | handleItemChange = e => {
35 | this.setState({ item: e.target.value })
36 | }
37 |
38 | handlePriorityChange = e => {
39 | this.setState({ priority: e.target.value })
40 | }
41 |
42 | addItem = e => {
43 | e.preventDefault()
44 | const { priorityQueue, item, priority } = this.state
45 | if (item && priority) {
46 | priorityQueue.enqueue(item, Number(priority || 1))
47 | this.setState(
48 | { priorityQueue, item: '', priority: 1 },
49 | () => this.textInputRef.current && this.textInputRef.current.focus()
50 | )
51 | }
52 | }
53 |
54 | removeItem = () => {
55 | const { priorityQueue } = this.state
56 | priorityQueue.dequeue()
57 | this.setState({ priorityQueue })
58 | }
59 |
60 | clearPriorityQueue = () => {
61 | const { priorityQueue } = this.state
62 | priorityQueue.clear()
63 | this.setState({ priorityQueue })
64 | }
65 |
66 | render() {
67 | const { priorityQueue, item, priority } = this.state
68 |
69 | return (
70 |
71 |
Priority Queue Demo
72 |
73 |
74 |
115 |
116 |
117 |
Priority Queue Contents:
118 |
119 |
Front of Priority Queue
120 | {priorityQueue.isEmpty() ? (
121 |
(currently empty)
122 | ) : (
123 | priorityQueue.enumerate().map((item, index) => {
124 | return (
125 |
130 | )
131 | })
132 | )}
133 |
End of Priority Queue
134 |
135 |
136 |
137 |
138 | )
139 | }
140 | }
141 |
142 | export const priorityQueueVisualizer = () =>
143 |
--------------------------------------------------------------------------------
/src/data-structures/priority-queue/src/priority-queue.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Priority Queue - highest priority get dequeued first, like a list of calls to 911 dispatchers
3 | *
4 | * NOTE: For this implementation, we'll assume a lower number means a higher priority.
5 | * i.e. An item with priority 1 would be dequeued before an item with priority 3
6 | *
7 | * Methods and properties:
8 | *
9 | * - enqueue: Constant — O(1)
10 | * - dequeue: Constant — O(1)
11 | * - peek: Constant — O(1)
12 | * - isEmpty: Constant — O(1)
13 | * - size: Constant — O(1)
14 | * - enumerate: Linear - O(n)
15 | * - clear: Constant - O(1)
16 | */
17 |
18 | export class PriorityQueue {
19 | constructor() {
20 | this.items = []
21 | this.length = 0
22 | }
23 |
24 | enqueue(value, priority = 0) {
25 | // handle inserting the new item in the right place according to priority
26 | for (let i = 0; i < this.length; i++) {
27 | if (priority < this.items[i].priority) {
28 | const item = { value, priority }
29 | this.items.splice(i, 0, item)
30 | this.length++
31 | return item
32 | }
33 | }
34 |
35 | // if we've iterated through the entire priority queue,
36 | // then just add the new value at the end
37 | const item = { value, priority }
38 | this.items.push(item)
39 | this.length++
40 | return item
41 | }
42 |
43 | dequeue() {
44 | if (this.length) {
45 | this.length--
46 | return this.items.shift()
47 | } else {
48 | return null
49 | }
50 | }
51 |
52 | peek() {
53 | return this.items[0] || null
54 | }
55 |
56 | isEmpty() {
57 | return this.length === 0
58 | }
59 |
60 | size() {
61 | return this.length
62 | }
63 |
64 | enumerate() {
65 | return this.items
66 | }
67 |
68 | clear() {
69 | this.length = 0
70 | this.items = []
71 | return this.items
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/data-structures/queue/demo/queue-performance-test.demo.css:
--------------------------------------------------------------------------------
1 | .queuePerformanceTestDemo button {
2 | margin-right: 0.5rem;
3 | margin-bottom: 0.5rem;
4 | }
5 |
--------------------------------------------------------------------------------
/src/data-structures/queue/demo/queue.demo.css:
--------------------------------------------------------------------------------
1 | .queueDemo .flexContainer {
2 | display: flex;
3 | flex-direction: row;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .queueDemo .flexContainer .leftColumn {
8 | margin-right: 2rem;
9 | }
10 |
11 | .queueDemo .flexContainer .rightColumn {
12 | flex-shrink: 0;
13 | }
14 |
15 | .queueDemo .queueContainer {
16 | width: 17rem;
17 | border: 1px solid #071a32;
18 | border-radius: 0.25rem;
19 | padding: 1rem;
20 | margin-bottom: 0.25rem;
21 | }
22 |
23 | .queueDemo h2 {
24 | margin-top: 0;
25 | }
26 |
27 | .queueDemo .item {
28 | padding: 1rem;
29 | margin-bottom: 0.25rem;
30 | border: 1px solid #071a32;
31 | border-radius: 0.25rem;
32 | background: #f2f2f2;
33 | color: #071a32;
34 | text-align: center;
35 | word-break: break-word;
36 | word-wrap: break-word;
37 | min-width: 0;
38 | }
39 |
40 | .queueDemo .item.alt {
41 | background: #ccc;
42 | }
43 |
44 | .queueDemo .newItemTextInput {
45 | margin-right: 0.5rem;
46 | }
47 |
48 | .queueDemo button {
49 | margin-bottom: 0.5rem;
50 | }
51 |
52 | .queueDemo .frontOfQueue {
53 | border-bottom: 1px solid #071a32;
54 | font-weight: bold;
55 | }
56 |
57 | .queueDemo .endOfQueue {
58 | border-top: 1px solid #071a32;
59 | font-weight: bold;
60 | }
61 |
--------------------------------------------------------------------------------
/src/data-structures/queue/demo/queue.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component, createRef } from 'react'
2 | import { Queue } from '../src/queue'
3 | import { QueuePerformanceTest } from './queue-performance-test.demo'
4 | import './queue.demo.css'
5 |
6 | export default {
7 | title: 'Data Structures/Queue',
8 | }
9 |
10 | const Item = ({ value, className }) => {
11 | return {value}
12 | }
13 |
14 | const generateOddOrEvenClassName = index =>
15 | index % 2 === 0 ? 'item' : 'item alt'
16 |
17 | class QueueVisualizer extends Component {
18 | state = {
19 | queue: new Queue(),
20 | item: '',
21 | }
22 |
23 | textInputRef = createRef()
24 |
25 | handleItemChange = e => {
26 | this.setState({ item: e.target.value })
27 | }
28 |
29 | addItem = e => {
30 | e.preventDefault()
31 | const { queue, item } = this.state
32 | if (item) {
33 | queue.enqueue(item)
34 | this.setState(
35 | { queue, item: '' },
36 | () => this.textInputRef.current && this.textInputRef.current.focus()
37 | )
38 | }
39 | }
40 |
41 | removeItem = () => {
42 | const { queue } = this.state
43 | queue.dequeue()
44 | this.setState({ queue })
45 | }
46 |
47 | clearQueue = () => {
48 | const { queue } = this.state
49 | queue.clear()
50 | this.setState({ queue })
51 | }
52 |
53 | render() {
54 | const { queue, item } = this.state
55 |
56 | return (
57 |
58 |
Queue Demo
59 |
60 |
61 |
88 |
89 |
90 |
Queue Contents:
91 |
92 |
Front of Queue
93 | {queue.isEmpty() ? (
94 |
(currently empty)
95 | ) : (
96 | queue.enumerate().map((value, index) => {
97 | return (
98 |
103 | )
104 | })
105 | )}
106 |
End of Queue
107 |
108 |
109 |
110 |
111 | )
112 | }
113 | }
114 |
115 | export const queueVisualizer = () =>
116 |
117 | export const performanceTest = () =>
118 |
--------------------------------------------------------------------------------
/src/data-structures/queue/src/queue-from-doubly-linked-list.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Queue - FIFO, like a checkout line at a grocery store
3 | *
4 | * NOTE: This queue is implemented with a DoublyLinkedList as its underlying data structure.
5 | *
6 | * Methods and properties:
7 | *
8 | * - enqueue: Constant — O(1)
9 | * - dequeue: Constant — O(1)
10 | * - peek: Constant — O(1)
11 | * - isEmpty: Constant — O(1)
12 | * - size: Constant — O(1)
13 | * - enumerate: Linear - O(n)
14 | * - clear: Constant - O(1)
15 | */
16 |
17 | import { DoublyLinkedList } from '../../doubly-linked-list/src/doubly-linked-list'
18 |
19 | export class QueueFromDoublyLinkedList {
20 | constructor() {
21 | this.queue = new DoublyLinkedList()
22 | }
23 |
24 | enqueue(val) {
25 | this.queue.insertAtEnd(val)
26 | return val
27 | }
28 |
29 | dequeue() {
30 | const dequeuedNode = this.queue.getAt(0)
31 | this.queue.deleteFirstNode()
32 | return dequeuedNode ? dequeuedNode.val : null
33 | }
34 |
35 | peek() {
36 | const peekedNode = this.queue.getAt(0)
37 | return peekedNode ? peekedNode.val : null
38 | }
39 |
40 | isEmpty() {
41 | return this.queue.isEmpty()
42 | }
43 |
44 | size() {
45 | return this.queue.size()
46 | }
47 |
48 | enumerate() {
49 | return this.queue.enumerate()
50 | }
51 |
52 | clear() {
53 | this.queue.clear()
54 | // for consistency with the Queue that uses an array as its underlying data structure
55 | return []
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/data-structures/queue/src/queue.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Queue - FIFO, like a checkout line at a grocery store
3 | *
4 | * NOTE: This queue is implemented with an array as its underlying data structure.
5 | *
6 | * Methods and properties:
7 | *
8 | * - enqueue: Constant — O(1)
9 | * - dequeue: Constant — O(1)
10 | * - peek: Constant — O(1)
11 | * - isEmpty: Constant — O(1)
12 | * - size: Constant — O(1)
13 | * - enumerate: Linear - O(n)
14 | * - clear: Constant - O(1)
15 | */
16 |
17 | export class Queue {
18 | constructor() {
19 | this.items = []
20 | this.length = 0
21 | }
22 |
23 | enqueue(val) {
24 | this.items.push(val)
25 | this.length++
26 | return val
27 | }
28 |
29 | dequeue() {
30 | if (this.length) {
31 | this.length--
32 | return this.items.shift()
33 | } else {
34 | return null
35 | }
36 | }
37 |
38 | peek() {
39 | return this.items[0] || null
40 | }
41 |
42 | isEmpty() {
43 | return this.length === 0
44 | }
45 |
46 | size() {
47 | return this.length
48 | }
49 |
50 | enumerate() {
51 | return this.items
52 | }
53 |
54 | clear() {
55 | this.length = 0
56 | this.items = []
57 | return this.items
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/data-structures/queue/src/queue.test.js:
--------------------------------------------------------------------------------
1 | import { Queue } from './queue'
2 |
3 | describe('Queue', () => {
4 | it('can be instantiated with the `new` keyword', () => {
5 | expect(() => new Queue()).not.toThrow()
6 | })
7 |
8 | describe('enqueue', () => {
9 | it('can have items added to it', () => {
10 | const queue1 = new Queue()
11 | queue1.enqueue(42)
12 | expect(queue1.size()).toBe(1)
13 |
14 | queue1.enqueue(10)
15 | expect(queue1.size()).toBe(2)
16 | })
17 |
18 | it('returns the item that is added to the queue', () => {
19 | const queue1 = new Queue()
20 | expect(queue1.enqueue(42)).toBe(42)
21 | expect(queue1.enqueue(10)).toBe(10)
22 | })
23 |
24 | it('can have items with the same value added to it', () => {
25 | const queue1 = new Queue()
26 | queue1.enqueue(42)
27 | queue1.enqueue(42)
28 | queue1.enqueue(42)
29 | expect(queue1.size()).toBe(3)
30 | expect(queue1.enumerate()).toEqual([42, 42, 42])
31 | })
32 | })
33 |
34 | describe('dequeue', () => {
35 | it('can have items removed from it', () => {
36 | const queue1 = new Queue()
37 | queue1.enqueue(42)
38 | queue1.enqueue(10)
39 | queue1.enqueue('a')
40 | expect(queue1.size()).toBe(3)
41 |
42 | queue1.dequeue()
43 | expect(queue1.size()).toBe(2)
44 |
45 | queue1.dequeue()
46 | expect(queue1.size()).toBe(1)
47 |
48 | queue1.dequeue()
49 | expect(queue1.size()).toBe(0)
50 | })
51 |
52 | it('does not throw when trying to remove items from an empty queue', () => {
53 | const queue1 = new Queue()
54 | expect(() => queue1.dequeue()).not.toThrow()
55 | })
56 |
57 | it('returns the item that is removed from the queue', () => {
58 | const queue1 = new Queue()
59 | expect(queue1.dequeue()).toBe(null)
60 |
61 | queue1.enqueue(42)
62 | expect(queue1.dequeue()).toBe(42)
63 | })
64 | })
65 |
66 | describe('peek', () => {
67 | it('returns the top element in the queue without modifying the queue', () => {
68 | const queue1 = new Queue()
69 | queue1.enqueue(42)
70 | queue1.enqueue(10)
71 |
72 | expect(queue1.size()).toBe(2)
73 | expect(queue1.peek()).toBe(42)
74 | expect(queue1.size()).toBe(2)
75 | })
76 |
77 | it('returns null if the queue is empty', () => {
78 | const queue1 = new Queue()
79 | expect(queue1.peek()).toBe(null)
80 | })
81 | })
82 |
83 | describe('isEmpty', () => {
84 | it('returns true if the queue is empty', () => {
85 | const queue1 = new Queue()
86 | expect(queue1.isEmpty()).toBe(true)
87 | })
88 |
89 | it('returns false if the queue is not empty', () => {
90 | const queue1 = new Queue()
91 | queue1.enqueue(42)
92 | expect(queue1.isEmpty()).toBe(false)
93 | })
94 | })
95 |
96 | describe('size', () => {
97 | it('returns the correct number of elements in the queue', () => {
98 | const queue1 = new Queue()
99 | expect(queue1.size()).toBe(0)
100 |
101 | queue1.enqueue(42)
102 | expect(queue1.size()).toBe(1)
103 |
104 | queue1.enqueue(10)
105 | expect(queue1.size()).toBe(2)
106 |
107 | queue1.enqueue('a')
108 | expect(queue1.size()).toBe(3)
109 |
110 | queue1.dequeue()
111 | expect(queue1.size()).toBe(2)
112 |
113 | queue1.dequeue()
114 | expect(queue1.size()).toBe(1)
115 |
116 | queue1.dequeue()
117 | expect(queue1.size()).toBe(0)
118 | })
119 | })
120 |
121 | describe('enumerate', () => {
122 | it('returns the entire queue in the correct FIFO order', () => {
123 | const queue1 = new Queue()
124 | queue1.enqueue(42)
125 | queue1.enqueue(10)
126 | queue1.enqueue('a')
127 | expect(queue1.enumerate()).toEqual([42, 10, 'a'])
128 | })
129 |
130 | it('returns an empty array when an empty queue is enumerated', () => {
131 | const queue1 = new Queue()
132 | expect(queue1.enumerate()).toEqual([])
133 | })
134 | })
135 |
136 | describe('clear', () => {
137 | it('removes all elements from the queue', () => {
138 | const queue1 = new Queue()
139 | queue1.enqueue(42)
140 | queue1.enqueue(10)
141 | queue1.enqueue('a')
142 | expect(queue1.clear()).toEqual([])
143 | })
144 |
145 | it('sets the queue size to 0 when emptied', () => {
146 | const queue1 = new Queue()
147 | queue1.enqueue(42)
148 | queue1.enqueue(10)
149 | queue1.enqueue('a')
150 | queue1.clear()
151 | expect(queue1.size()).toEqual(0)
152 | })
153 | })
154 | })
155 |
--------------------------------------------------------------------------------
/src/data-structures/set/demo/set.demo.css:
--------------------------------------------------------------------------------
1 | .setDemo .flexContainer {
2 | display: flex;
3 | flex-direction: row;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .setDemo .flexContainer .leftColumn {
8 | margin-right: 2rem;
9 | }
10 |
11 | .setDemo .flexContainer .rightColumn {
12 | flex-shrink: 0;
13 | }
14 |
15 | .setDemo .setContainer {
16 | width: 17rem;
17 | border: 1px solid #071a32;
18 | border-radius: 0.25rem;
19 | padding: 1rem;
20 | margin-bottom: 0.25rem;
21 | }
22 |
23 | .setDemo h2 {
24 | margin-top: 0;
25 | }
26 |
27 | .setDemo .item {
28 | padding: 1rem;
29 | margin-bottom: 0.25rem;
30 | border: 1px solid #071a32;
31 | border-radius: 0.25rem;
32 | background: #f2f2f2;
33 | color: #071a32;
34 | text-align: center;
35 | word-break: break-word;
36 | word-wrap: break-word;
37 | min-width: 0;
38 | }
39 |
40 | .setDemo .item.alt {
41 | background: #ccc;
42 | }
43 |
44 | .setDemo .newItemTextInput,
45 | .setDemo .removeItemTextInput {
46 | margin-right: 0.5rem;
47 | }
48 |
49 | .setDemo button {
50 | margin-bottom: 0.5rem;
51 | }
52 |
--------------------------------------------------------------------------------
/src/data-structures/set/demo/set.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component, createRef } from 'react'
2 | import { Set } from '../src/set'
3 | import './set.demo.css'
4 |
5 | export default {
6 | title: 'Data Structures/Set',
7 | }
8 |
9 | const Item = ({ value, className }) => {
10 | return {value}
11 | }
12 |
13 | const generateOddOrEvenClassName = index =>
14 | index % 2 === 0 ? 'item' : 'item alt'
15 |
16 | class SetVisualizer extends Component {
17 | state = {
18 | set: new Set(),
19 | itemToAdd: '',
20 | itemToRemove: '',
21 | }
22 |
23 | addItemInputRef = createRef()
24 | removeItemInputRef = createRef()
25 |
26 | handleItemToAddChange = e => {
27 | this.setState({ itemToAdd: e.target.value })
28 | }
29 |
30 | addItem = e => {
31 | e.preventDefault()
32 | const { set, itemToAdd } = this.state
33 | if (itemToAdd) {
34 | set.add(itemToAdd)
35 | this.setState(
36 | { set, itemToAdd: '' },
37 | () =>
38 | this.addItemInputRef.current && this.addItemInputRef.current.focus()
39 | )
40 | }
41 | }
42 |
43 | handleItemToRemoveChange = e => {
44 | this.setState({ itemToRemove: e.target.value })
45 | }
46 |
47 | removeItem = e => {
48 | e.preventDefault()
49 | const { set, itemToRemove } = this.state
50 | set.remove(itemToRemove)
51 | this.setState(
52 | { set, itemToRemove: '' },
53 | () =>
54 | this.removeItemInputRef.current &&
55 | this.removeItemInputRef.current.focus()
56 | )
57 | }
58 |
59 | clearSet = () => {
60 | const { set } = this.state
61 | set.clear()
62 | this.setState({ set })
63 | }
64 |
65 | render() {
66 | const { set, itemToAdd, itemToRemove } = this.state
67 |
68 | return (
69 |
70 |
Set Demo
71 |
72 |
73 |
84 |
103 |
104 |
105 |
Set Contents:
106 |
107 | {set.isEmpty() ? (
108 |
(currently empty)
109 | ) : (
110 | set.enumerate().map((value, index) => {
111 | return (
112 |
117 | )
118 | })
119 | )}
120 |
121 |
122 |
123 |
124 | )
125 | }
126 | }
127 |
128 | export const setVisualizer = () =>
129 |
--------------------------------------------------------------------------------
/src/data-structures/set/src/set.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set - A collection of unique items (no duplicates allowed)
3 | *
4 | * Methods and properties:
5 | *
6 | * - add: Constant — O(1)
7 | * - remove: Linear — O(n)
8 | * - has: Linear — O(n)
9 | * - isEmpty: Constant — O(1)
10 | * - size: Constant — O(1)
11 | * - enumerate: Linear - O(n)
12 | * - clear: Constant - O(1)
13 | */
14 |
15 | export class Set {
16 | constructor() {
17 | this.items = []
18 | this.length = 0
19 | }
20 |
21 | add(val) {
22 | if (this.items.indexOf(val) === -1) {
23 | this.items.push(val)
24 | this.length++
25 | }
26 | return val
27 | }
28 |
29 | remove(val) {
30 | const index = this.items.indexOf(val)
31 | if (index !== -1) {
32 | this.items.splice(index, 1)
33 | this.length--
34 | }
35 | return val
36 | }
37 |
38 | has(val) {
39 | return this.items.indexOf(val) !== -1
40 | }
41 |
42 | isEmpty() {
43 | return this.length === 0
44 | }
45 |
46 | size() {
47 | return this.length
48 | }
49 |
50 | enumerate() {
51 | return this.items
52 | }
53 |
54 | clear() {
55 | this.length = 0
56 | this.items = []
57 | return this.items
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/data-structures/set/src/set.test.js:
--------------------------------------------------------------------------------
1 | import { Set } from './set'
2 |
3 | describe('Set', () => {
4 | it('can be instantiated with the `new` keyword', () => {
5 | expect(() => new Set()).not.toThrow()
6 | })
7 |
8 | describe('add', () => {
9 | it('can have items added to it', () => {
10 | const set1 = new Set()
11 | set1.add(42)
12 | expect(set1.size()).toBe(1)
13 |
14 | set1.add(10)
15 | expect(set1.size()).toBe(2)
16 | })
17 |
18 | it('returns the item that is added to the set', () => {
19 | const set1 = new Set()
20 | expect(set1.add(42)).toBe(42)
21 | expect(set1.add(10)).toBe(10)
22 | })
23 |
24 | it('does not add duplicate items to the set', () => {
25 | const set1 = new Set()
26 | set1.add(42)
27 | expect(set1.size()).toBe(1)
28 | set1.add(42)
29 | expect(set1.size()).toBe(1)
30 | expect(set1.enumerate()).toEqual([42])
31 | })
32 | })
33 |
34 | describe('remove', () => {
35 | it('can have items removed from it', () => {
36 | const set1 = new Set()
37 | set1.add(42)
38 | set1.add(10)
39 | set1.add('a')
40 | expect(set1.size()).toBe(3)
41 |
42 | set1.remove(10)
43 | expect(set1.size()).toBe(2)
44 |
45 | set1.remove('a')
46 | expect(set1.size()).toBe(1)
47 |
48 | set1.remove(42)
49 | expect(set1.size()).toBe(0)
50 | })
51 |
52 | it('does not throw when trying to remove items from an empty set', () => {
53 | const set1 = new Set()
54 | expect(() => set1.remove(42)).not.toThrow()
55 | })
56 |
57 | it('does not throw when trying to remove an item that does not exist in the set', () => {
58 | const set1 = new Set()
59 | set1.add(42)
60 | expect(() => set1.remove(10)).not.toThrow()
61 | expect(set1.size()).toBe(1)
62 | })
63 |
64 | it('returns the item that is removed from the set', () => {
65 | const set1 = new Set()
66 | expect(set1.remove(10)).toBe(10)
67 |
68 | set1.add(42)
69 | expect(set1.remove(42)).toBe(42)
70 | })
71 | })
72 |
73 | describe('has', () => {
74 | it('returns true if the set has the specified item', () => {
75 | const set1 = new Set()
76 | set1.add(42)
77 | expect(set1.has(42)).toBe(true)
78 | })
79 |
80 | it('returns false if the set does not have the specified item', () => {
81 | const set1 = new Set()
82 | set1.add(42)
83 | expect(set1.has(10)).toBe(false)
84 | })
85 | })
86 |
87 | describe('isEmpty', () => {
88 | it('returns true if the set is empty', () => {
89 | const set1 = new Set()
90 | expect(set1.isEmpty()).toBe(true)
91 | })
92 |
93 | it('returns false if the set is not empty', () => {
94 | const set1 = new Set()
95 | set1.add(42)
96 | expect(set1.isEmpty()).toBe(false)
97 | })
98 | })
99 |
100 | describe('size', () => {
101 | it('returns the correct number of elements in the set', () => {
102 | const set1 = new Set()
103 | expect(set1.size()).toBe(0)
104 |
105 | set1.add(42)
106 | expect(set1.size()).toBe(1)
107 |
108 | set1.add(10)
109 | expect(set1.size()).toBe(2)
110 |
111 | set1.add('a')
112 | expect(set1.size()).toBe(3)
113 |
114 | set1.remove(42)
115 | expect(set1.size()).toBe(2)
116 |
117 | set1.remove(10)
118 | expect(set1.size()).toBe(1)
119 |
120 | set1.remove('a')
121 | expect(set1.size()).toBe(0)
122 | })
123 | })
124 |
125 | describe('enumerate', () => {
126 | it('returns the entire set', () => {
127 | const set1 = new Set()
128 | set1.add(42)
129 | set1.add(10)
130 | set1.add('a')
131 | expect(set1.enumerate()).toEqual([42, 10, 'a'])
132 | })
133 |
134 | it('returns an empty array when an empty set is enumerated', () => {
135 | const set1 = new Set()
136 | expect(set1.enumerate()).toEqual([])
137 | })
138 | })
139 |
140 | describe('clear', () => {
141 | it('removes all elements from the set', () => {
142 | const set1 = new Set()
143 | set1.add(42)
144 | set1.add(10)
145 | set1.add('a')
146 | expect(set1.clear()).toEqual([])
147 | })
148 |
149 | it('sets the set size to 0 when emptied', () => {
150 | const set1 = new Set()
151 | set1.add(42)
152 | set1.add(10)
153 | set1.add('a')
154 | set1.clear()
155 | expect(set1.size()).toEqual(0)
156 | })
157 | })
158 | })
159 |
--------------------------------------------------------------------------------
/src/data-structures/stack/demo/stack-performance-test.demo.css:
--------------------------------------------------------------------------------
1 | .stackPerformanceTestDemo button {
2 | margin-right: 0.5rem;
3 | margin-bottom: 0.5rem;
4 | }
5 |
--------------------------------------------------------------------------------
/src/data-structures/stack/demo/stack.demo.css:
--------------------------------------------------------------------------------
1 | .stackDemo .flexContainer {
2 | display: flex;
3 | flex-direction: row;
4 | flex-wrap: wrap;
5 | }
6 |
7 | .stackDemo .flexContainer .leftColumn {
8 | margin-right: 2rem;
9 | }
10 |
11 | .stackDemo .flexContainer .rightColumn {
12 | flex-shrink: 0;
13 | }
14 |
15 | .stackDemo .stackContainer {
16 | width: 17rem;
17 | border: 1px solid #071a32;
18 | border-radius: 0.25rem;
19 | padding: 1rem;
20 | margin-bottom: 0.25rem;
21 | }
22 |
23 | .stackDemo h2 {
24 | margin-top: 0;
25 | }
26 |
27 | .stackDemo .item {
28 | padding: 1rem;
29 | margin-bottom: 0.25rem;
30 | border: 1px solid #071a32;
31 | border-radius: 0.25rem;
32 | background: #f2f2f2;
33 | color: #071a32;
34 | text-align: center;
35 | word-break: break-word;
36 | word-wrap: break-word;
37 | min-width: 0;
38 | }
39 |
40 | .stackDemo .item.alt {
41 | background: #ccc;
42 | }
43 |
44 | .stackDemo .newItemTextInput {
45 | margin-right: 0.5rem;
46 | }
47 |
48 | .stackDemo button {
49 | margin-bottom: 0.5rem;
50 | }
51 |
52 | .stackDemo .topOfStack {
53 | border-bottom: 1px solid #071a32;
54 | font-weight: bold;
55 | }
56 |
57 | .stackDemo .bottomOfStack {
58 | border-top: 1px solid #071a32;
59 | font-weight: bold;
60 | }
61 |
--------------------------------------------------------------------------------
/src/data-structures/stack/demo/stack.demo.js:
--------------------------------------------------------------------------------
1 | import React, { Component, createRef } from 'react'
2 | import { Stack } from '../src/stack'
3 | import { StackPerformanceTest } from './stack-performance-test.demo'
4 | import './stack.demo.css'
5 |
6 | export default {
7 | title: 'Data Structures/Stack',
8 | }
9 |
10 | const Item = ({ value, className }) => {
11 | return {value}
12 | }
13 |
14 | const generateOddOrEvenClassName = (index, totalNumberOfItems) => {
15 | if (index % 2 === 0) {
16 | return totalNumberOfItems % 2 === 0 ? 'item alt' : 'item'
17 | } else {
18 | return totalNumberOfItems % 2 === 0 ? 'item' : 'item alt'
19 | }
20 | }
21 |
22 | class StackVisualizer extends Component {
23 | state = {
24 | stack: new Stack(),
25 | item: '',
26 | }
27 |
28 | textInputRef = createRef()
29 |
30 | handleItemChange = e => {
31 | this.setState({ item: e.target.value })
32 | }
33 |
34 | addItem = e => {
35 | e.preventDefault()
36 | const { stack, item } = this.state
37 | if (item) {
38 | stack.push(item)
39 | this.setState(
40 | { stack, item: '' },
41 | () => this.textInputRef.current && this.textInputRef.current.focus()
42 | )
43 | }
44 | }
45 |
46 | removeItem = () => {
47 | const { stack } = this.state
48 | stack.pop()
49 | this.setState({ stack })
50 | }
51 |
52 | clearStack = () => {
53 | const { stack } = this.state
54 | stack.clear()
55 | this.setState({ stack })
56 | }
57 |
58 | render() {
59 | const { stack, item } = this.state
60 |
61 | return (
62 |
63 |
Stack Demo
64 |
65 |
66 |
93 |
94 |
95 |
Stack Contents:
96 |
97 |
Top of Stack
98 | {stack.isEmpty() ? (
99 |
(currently empty)
100 | ) : (
101 | stack.enumerate().map((value, index, allItems) => {
102 | return (
103 |
111 | )
112 | })
113 | )}
114 |
Bottom of Stack
115 |
116 |
117 |
118 |
119 | )
120 | }
121 | }
122 |
123 | export const stackVisualizer = () =>
124 |
125 | export const performanceTest = () =>
126 |
--------------------------------------------------------------------------------
/src/data-structures/stack/src/stack-from-linked-list.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Stack - LIFO, like a stack of dishes
3 | *
4 | * NOTE: This stack is implemented with a LinkedList as its underlying data structure.
5 | *
6 | * Methods and properties:
7 | *
8 | * - push: Constant — O(1)
9 | * - pop: Constant — O(1)
10 | * - peek: Constant — O(1)
11 | * - isEmpty: Constant — O(1)
12 | * - size: Constant — O(1)
13 | * - enumerate: Linear - O(n)
14 | * - clear: Constant - O(1)
15 | */
16 |
17 | import { LinkedList } from '../../linked-list/src/linked-list'
18 |
19 | export class StackFromLinkedList {
20 | constructor() {
21 | this.stack = new LinkedList()
22 | }
23 |
24 | push(val) {
25 | this.stack.insertAtBeginning(val)
26 | return val
27 | }
28 |
29 | pop() {
30 | const poppedNode = this.stack.getAt(0)
31 | this.stack.deleteFirstNode()
32 | return poppedNode ? poppedNode.val : null
33 | }
34 |
35 | peek() {
36 | const peekedNode = this.stack.getAt(0)
37 | return peekedNode ? peekedNode.val : null
38 | }
39 |
40 | isEmpty() {
41 | return this.stack.isEmpty()
42 | }
43 |
44 | size() {
45 | return this.stack.size()
46 | }
47 |
48 | enumerate() {
49 | return this.stack.enumerate()
50 | }
51 |
52 | clear() {
53 | this.stack.clear()
54 | // for consistency with the Stack that uses an array as its underlying data structure
55 | return []
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/data-structures/stack/src/stack-from-linked-list.test.js:
--------------------------------------------------------------------------------
1 | import { StackFromLinkedList } from './stack-from-linked-list'
2 |
3 | describe('StackFromLinkedList', () => {
4 | it('can be instantiated with the `new` keyword', () => {
5 | expect(() => new StackFromLinkedList()).not.toThrow()
6 | })
7 |
8 | describe('push', () => {
9 | it('can have items added to it', () => {
10 | const stack1 = new StackFromLinkedList()
11 | stack1.push(42)
12 | expect(stack1.size()).toBe(1)
13 |
14 | stack1.push(10)
15 | expect(stack1.size()).toBe(2)
16 | })
17 |
18 | it('returns the item that is added to the stack', () => {
19 | const stack1 = new StackFromLinkedList()
20 | expect(stack1.push(42)).toBe(42)
21 | expect(stack1.push(10)).toBe(10)
22 | })
23 |
24 | it('can have items added to it with the same value', () => {
25 | const stack1 = new StackFromLinkedList()
26 | stack1.push(42)
27 | stack1.push(42)
28 | stack1.push(42)
29 | expect(stack1.size()).toBe(3)
30 | expect(stack1.enumerate()).toEqual([42, 42, 42])
31 | })
32 | })
33 |
34 | describe('pop', () => {
35 | it('can have items removed from it', () => {
36 | const stack1 = new StackFromLinkedList()
37 | stack1.push(42)
38 | stack1.push(10)
39 | stack1.push('a')
40 | expect(stack1.size()).toBe(3)
41 |
42 | stack1.pop()
43 | expect(stack1.size()).toBe(2)
44 |
45 | stack1.pop()
46 | expect(stack1.size()).toBe(1)
47 |
48 | stack1.pop()
49 | expect(stack1.size()).toBe(0)
50 | })
51 |
52 | it('does not throw when trying to remove items from an empty stack', () => {
53 | const stack1 = new StackFromLinkedList()
54 | expect(() => stack1.pop()).not.toThrow()
55 | })
56 |
57 | it('returns the item that is removed from the stack', () => {
58 | const stack1 = new StackFromLinkedList()
59 | expect(stack1.pop()).toBe(null)
60 |
61 | stack1.push(42)
62 | expect(stack1.pop()).toBe(42)
63 | })
64 | })
65 |
66 | describe('peek', () => {
67 | it('returns the top element in the stack without modifying the stack', () => {
68 | const stack1 = new StackFromLinkedList()
69 | stack1.push(42)
70 | stack1.push(10)
71 |
72 | expect(stack1.size()).toBe(2)
73 | expect(stack1.peek()).toBe(10)
74 | expect(stack1.size()).toBe(2)
75 | })
76 |
77 | it('returns null if the stack is empty', () => {
78 | const stack1 = new StackFromLinkedList()
79 | expect(stack1.peek()).toBe(null)
80 | })
81 | })
82 |
83 | describe('isEmpty', () => {
84 | it('returns true if the stack is empty', () => {
85 | const stack1 = new StackFromLinkedList()
86 | expect(stack1.isEmpty()).toBe(true)
87 | })
88 |
89 | it('returns false if the stack is not empty', () => {
90 | const stack1 = new StackFromLinkedList()
91 | stack1.push(42)
92 | expect(stack1.isEmpty()).toBe(false)
93 | })
94 | })
95 |
96 | describe('size', () => {
97 | it('returns the correct number of elements in the stack', () => {
98 | const stack1 = new StackFromLinkedList()
99 | expect(stack1.size()).toBe(0)
100 |
101 | stack1.push(42)
102 | expect(stack1.size()).toBe(1)
103 |
104 | stack1.push(10)
105 | expect(stack1.size()).toBe(2)
106 |
107 | stack1.push('a')
108 | expect(stack1.size()).toBe(3)
109 |
110 | stack1.pop()
111 | expect(stack1.size()).toBe(2)
112 |
113 | stack1.pop()
114 | expect(stack1.size()).toBe(1)
115 |
116 | stack1.pop()
117 | expect(stack1.size()).toBe(0)
118 | })
119 | })
120 |
121 | describe('enumerate', () => {
122 | it('returns the entire stack in the correct LIFO order', () => {
123 | const stack1 = new StackFromLinkedList()
124 | stack1.push(42)
125 | stack1.push(10)
126 | stack1.push('a')
127 | expect(stack1.enumerate()).toEqual(['a', 10, 42])
128 | })
129 |
130 | it('returns an empty array when an empty stack is enumerated', () => {
131 | const stack1 = new StackFromLinkedList()
132 | expect(stack1.enumerate()).toEqual([])
133 | })
134 | })
135 |
136 | describe('clear', () => {
137 | it('removes all elements from the stack', () => {
138 | const stack1 = new StackFromLinkedList()
139 | stack1.push(42)
140 | stack1.push(10)
141 | stack1.push('a')
142 | expect(stack1.clear()).toEqual([])
143 | })
144 |
145 | it('sets the stack size to 0 when emptied', () => {
146 | const stack1 = new StackFromLinkedList()
147 | stack1.push(42)
148 | stack1.push(10)
149 | stack1.push('a')
150 | stack1.clear()
151 | expect(stack1.size()).toEqual(0)
152 | })
153 | })
154 | })
155 |
--------------------------------------------------------------------------------
/src/data-structures/stack/src/stack.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Stack - LIFO, like a stack of dishes
3 | *
4 | * NOTE: This stack is implemented with an array as its underlying data structure.
5 | *
6 | * Methods and properties:
7 | *
8 | * - push: Constant — O(1)
9 | * - pop: Constant — O(1)
10 | * - peek: Constant — O(1)
11 | * - isEmpty: Constant — O(1)
12 | * - size: Constant — O(1)
13 | * - enumerate: Linear - O(n)
14 | * - clear: Constant - O(1)
15 | */
16 |
17 | export class Stack {
18 | constructor() {
19 | this.items = []
20 | this.length = 0
21 | }
22 |
23 | push(val) {
24 | this.items.unshift(val)
25 | this.length++
26 | return val
27 | }
28 |
29 | pop() {
30 | if (this.length) {
31 | this.length--
32 | return this.items.shift()
33 | } else {
34 | return null
35 | }
36 | }
37 |
38 | peek() {
39 | return this.items[0] || null
40 | }
41 |
42 | isEmpty() {
43 | return this.length === 0
44 | }
45 |
46 | size() {
47 | return this.length
48 | }
49 |
50 | enumerate() {
51 | return this.items
52 | }
53 |
54 | clear() {
55 | this.length = 0
56 | this.items = []
57 | return this.items
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/data-structures/stack/src/stack.test.js:
--------------------------------------------------------------------------------
1 | import { Stack } from './stack'
2 |
3 | describe('Stack', () => {
4 | it('can be instantiated with the `new` keyword', () => {
5 | expect(() => new Stack()).not.toThrow()
6 | })
7 |
8 | describe('push', () => {
9 | it('can have items added to it', () => {
10 | const stack1 = new Stack()
11 | stack1.push(42)
12 | expect(stack1.size()).toBe(1)
13 |
14 | stack1.push(10)
15 | expect(stack1.size()).toBe(2)
16 | })
17 |
18 | it('returns the item that is added to the stack', () => {
19 | const stack1 = new Stack()
20 | expect(stack1.push(42)).toBe(42)
21 | expect(stack1.push(10)).toBe(10)
22 | })
23 |
24 | it('can have items added to it with the same value', () => {
25 | const stack1 = new Stack()
26 | stack1.push(42)
27 | stack1.push(42)
28 | stack1.push(42)
29 | expect(stack1.size()).toBe(3)
30 | expect(stack1.enumerate()).toEqual([42, 42, 42])
31 | })
32 | })
33 |
34 | describe('pop', () => {
35 | it('can have items removed from it', () => {
36 | const stack1 = new Stack()
37 | stack1.push(42)
38 | stack1.push(10)
39 | stack1.push('a')
40 | expect(stack1.size()).toBe(3)
41 |
42 | stack1.pop()
43 | expect(stack1.size()).toBe(2)
44 |
45 | stack1.pop()
46 | expect(stack1.size()).toBe(1)
47 |
48 | stack1.pop()
49 | expect(stack1.size()).toBe(0)
50 | })
51 |
52 | it('does not throw when trying to remove items from an empty stack', () => {
53 | const stack1 = new Stack()
54 | expect(() => stack1.pop()).not.toThrow()
55 | })
56 |
57 | it('returns the item that is removed from the stack', () => {
58 | const stack1 = new Stack()
59 | expect(stack1.pop()).toBe(null)
60 |
61 | stack1.push(42)
62 | expect(stack1.pop()).toBe(42)
63 | })
64 | })
65 |
66 | describe('peek', () => {
67 | it('returns the top element in the stack without modifying the stack', () => {
68 | const stack1 = new Stack()
69 | stack1.push(42)
70 | stack1.push(10)
71 |
72 | expect(stack1.size()).toBe(2)
73 | expect(stack1.peek()).toBe(10)
74 | expect(stack1.size()).toBe(2)
75 | })
76 |
77 | it('returns null if the stack is empty', () => {
78 | const stack1 = new Stack()
79 | expect(stack1.peek()).toBe(null)
80 | })
81 | })
82 |
83 | describe('isEmpty', () => {
84 | it('returns true if the stack is empty', () => {
85 | const stack1 = new Stack()
86 | expect(stack1.isEmpty()).toBe(true)
87 | })
88 |
89 | it('returns false if the stack is not empty', () => {
90 | const stack1 = new Stack()
91 | stack1.push(42)
92 | expect(stack1.isEmpty()).toBe(false)
93 | })
94 | })
95 |
96 | describe('size', () => {
97 | it('returns the correct number of elements in the stack', () => {
98 | const stack1 = new Stack()
99 | expect(stack1.size()).toBe(0)
100 |
101 | stack1.push(42)
102 | expect(stack1.size()).toBe(1)
103 |
104 | stack1.push(10)
105 | expect(stack1.size()).toBe(2)
106 |
107 | stack1.push('a')
108 | expect(stack1.size()).toBe(3)
109 |
110 | stack1.pop()
111 | expect(stack1.size()).toBe(2)
112 |
113 | stack1.pop()
114 | expect(stack1.size()).toBe(1)
115 |
116 | stack1.pop()
117 | expect(stack1.size()).toBe(0)
118 | })
119 | })
120 |
121 | describe('enumerate', () => {
122 | it('returns the entire stack in the correct LIFO order', () => {
123 | const stack1 = new Stack()
124 | stack1.push(42)
125 | stack1.push(10)
126 | stack1.push('a')
127 | expect(stack1.enumerate()).toEqual(['a', 10, 42])
128 | })
129 |
130 | it('returns an empty array when an empty stack is enumerated', () => {
131 | const stack1 = new Stack()
132 | expect(stack1.enumerate()).toEqual([])
133 | })
134 | })
135 |
136 | describe('clear', () => {
137 | it('removes all elements from the stack', () => {
138 | const stack1 = new Stack()
139 | stack1.push(42)
140 | stack1.push(10)
141 | stack1.push('a')
142 | expect(stack1.clear()).toEqual([])
143 | })
144 |
145 | it('sets the stack size to 0 when emptied', () => {
146 | const stack1 = new Stack()
147 | stack1.push(42)
148 | stack1.push(10)
149 | stack1.push('a')
150 | stack1.clear()
151 | expect(stack1.size()).toEqual(0)
152 | })
153 | })
154 | })
155 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | ////////////////////////
2 | // Algorithms
3 | ////////////////////////
4 |
5 | // Search (Array)
6 | export { binarySearch } from './algorithms/search/array/binary-search/src/binary-search'
7 | export { linearSearch } from './algorithms/search/array/linear-search/src/linear-search'
8 |
9 | // Search (String)
10 | export { boyerMooreHorspoolSearch } from './algorithms/search/string/boyer-moore-horspool-search/src/boyer-moore-horspool-search'
11 | export { naiveSearch } from './algorithms/search/string/naive-search/src/naive-search'
12 |
13 | // Set
14 | export { intersection } from './algorithms/set/intersection/src/intersection'
15 | export { setDifference } from './algorithms/set/set-difference/src/set-difference'
16 | export { symmetricDifference } from './algorithms/set/symmetric-difference/src/symmetric-difference'
17 | export { union } from './algorithms/set/union/src/union'
18 |
19 | // Sort
20 | export { bubbleSort } from './algorithms/sort/bubble-sort/src/bubble-sort'
21 | export { countingSort } from './algorithms/sort/counting-sort/src/counting-sort'
22 | export { insertionSort } from './algorithms/sort/insertion-sort/src/insertion-sort'
23 | export { mergeSort } from './algorithms/sort/merge-sort/src/merge-sort'
24 | export { quickSort } from './algorithms/sort/quick-sort/src/quick-sort'
25 | export { selectionSort } from './algorithms/sort/selection-sort/src/selection-sort'
26 |
27 | ////////////////////////
28 | // Data Structures
29 | ////////////////////////
30 |
31 | export { BinarySearchTree } from './data-structures/binary-search-tree/src/binary-search-tree'
32 | export { Node as BinarySearchTreeNode } from './data-structures/binary-search-tree/src/node'
33 | export { DoublyLinkedList } from './data-structures/doubly-linked-list/src/doubly-linked-list'
34 | export { Node as DoublyLinkedListNode } from './data-structures/doubly-linked-list/src/node'
35 | export { LinkedList } from './data-structures/linked-list/src/linked-list'
36 | export { Node as LinkedListNode } from './data-structures/linked-list/src/node'
37 | export { PriorityQueue } from './data-structures/priority-queue/src/priority-queue'
38 | export { Queue } from './data-structures/queue/src/queue'
39 | export { QueueFromDoublyLinkedList } from './data-structures/queue/src/queue-from-doubly-linked-list'
40 | export { Set } from './data-structures/set/src/set'
41 | export { Stack } from './data-structures/stack/src/stack'
42 | export { StackFromLinkedList } from './data-structures/stack/src/stack-from-linked-list'
43 |
--------------------------------------------------------------------------------
/storybook-dist/0.733027ac.iframe.bundle.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[0],{"./node_modules/@storybook/preview-web/dist/esm/renderDocs.js":function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,"renderDocs",(function(){return renderDocs})),__webpack_require__.d(__webpack_exports__,"unmountDocs",(function(){return unmountDocs}));__webpack_require__("./node_modules/regenerator-runtime/runtime.js"),__webpack_require__("./node_modules/core-js/modules/es.object.to-string.js"),__webpack_require__("./node_modules/core-js/modules/es.promise.js");var react=__webpack_require__("./node_modules/react/index.js"),react_default=__webpack_require__.n(react),react_dom=__webpack_require__("./node_modules/react-dom/index.js"),react_dom_default=__webpack_require__.n(react_dom),wrapper={fontSize:"14px",letterSpacing:"0.2px",margin:"10px 0"},main={margin:"auto",padding:30,borderRadius:10,background:"rgba(0,0,0,0.03)"},heading={textAlign:"center"},NoDocs_NoDocs=function NoDocs(){return react_default.a.createElement("div",{style:wrapper,className:"sb-nodocs sb-wrapper"},react_default.a.createElement("div",{style:main},react_default.a.createElement("h1",{style:heading},"No Docs"),react_default.a.createElement("p",null,"Sorry, but there are no docs for the selected story. To add them, set the story's ",react_default.a.createElement("code",null,"docs")," parameter. If you think this is an error:"),react_default.a.createElement("ul",null,react_default.a.createElement("li",null,"Please check the story definition."),react_default.a.createElement("li",null,"Please check the Storybook config."),react_default.a.createElement("li",null,"Try reloading the page.")),react_default.a.createElement("p",null,"If the problem persists, check the browser console, or the terminal you've run Storybook from.")))};function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))}}function renderDocs(story,docsContext,element,callback){return function renderDocsAsync(_x,_x2,_x3){return _renderDocsAsync.apply(this,arguments)}(story,docsContext,element).then(callback)}function _renderDocsAsync(){return(_renderDocsAsync=_asyncToGenerator(regeneratorRuntime.mark((function _callee(story,docsContext,element){var _docs$getContainer,_docs$getPage,docs,DocsContainer,Page,docsElement;return regeneratorRuntime.wrap((function _callee$(_context){for(;;)switch(_context.prev=_context.next){case 0:if(!(null!=(docs=story.parameters.docs)&&docs.getPage||null!=docs&&docs.page)||(null!=docs&&docs.getContainer||null!=docs&&docs.container)){_context.next=3;break}throw new Error("No `docs.container` set, did you run `addon-docs/preset`?");case 3:if(_context.t1=docs.container,_context.t1){_context.next=8;break}return _context.next=7,null===(_docs$getContainer=docs.getContainer)||void 0===_docs$getContainer?void 0:_docs$getContainer.call(docs);case 7:_context.t1=_context.sent;case 8:if(_context.t0=_context.t1,_context.t0){_context.next=11;break}_context.t0=function(_ref){var children=_ref.children;return react_default.a.createElement(react_default.a.Fragment,null,children)};case 11:if(DocsContainer=_context.t0,_context.t3=docs.page,_context.t3){_context.next=17;break}return _context.next=16,null===(_docs$getPage=docs.getPage)||void 0===_docs$getPage?void 0:_docs$getPage.call(docs);case 16:_context.t3=_context.sent;case 17:if(_context.t2=_context.t3,_context.t2){_context.next=20;break}_context.t2=NoDocs_NoDocs;case 20:return Page=_context.t2,docsElement=react_default.a.createElement(DocsContainer,{key:story.componentId,context:docsContext},react_default.a.createElement(Page,null)),_context.next=24,new Promise((function(resolve){react_dom_default.a.render(docsElement,element,resolve)}));case 24:case"end":return _context.stop()}}),_callee)})))).apply(this,arguments)}function unmountDocs(element){react_dom_default.a.unmountComponentAtNode(element)}NoDocs_NoDocs.displayName="NoDocs"}}]);
--------------------------------------------------------------------------------
/storybook-dist/10.908e40c81ef5762e4e6a.manager.bundle.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 |
9 | /**
10 | * @license
11 | * Copyright Google Inc. All Rights Reserved.
12 | *
13 | * Use of this source code is governed by an MIT-style license that can be
14 | * found in the LICENSE file at https://angular.io/license
15 | */
16 |
17 | /**
18 | * @license
19 | * Copyright Google Inc. All Rights Reserved.
20 | *
21 | * Use of this source code is governed by an MIT-style license that can be
22 | * found in the LICENSE file at https://angular.io/license
23 | */
24 |
25 | /**
26 | * @license
27 | * Copyright Google Inc. All Rights Reserved.
28 | *
29 | * Use of this source code is governed by an MIT-style license that can be
30 | * found in the LICENSE file at https://angular.io/license
31 | */
32 |
--------------------------------------------------------------------------------
/storybook-dist/13.f4916ecfc1ae8db3695f.manager.bundle.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*!
2 | * OverlayScrollbars
3 | * https://github.com/KingSora/OverlayScrollbars
4 | *
5 | * Version: 1.13.0
6 | *
7 | * Copyright KingSora | Rene Haas.
8 | * https://github.com/KingSora
9 | *
10 | * Released under the MIT license.
11 | * Date: 02.08.2020
12 | */
13 |
--------------------------------------------------------------------------------
/storybook-dist/7.332c6bddae98466fc74f.manager.bundle.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[7],{840:function(module,exports){module.exports=function(e,n){return n=n||{},new Promise((function(t,r){var s=new XMLHttpRequest,o=[],u=[],i={},a=function(){return{ok:2==(s.status/100|0),statusText:s.statusText,status:s.status,url:s.responseURL,text:function(){return Promise.resolve(s.responseText)},json:function(){return Promise.resolve(s.responseText).then(JSON.parse)},blob:function(){return Promise.resolve(new Blob([s.response]))},clone:a,headers:{keys:function(){return o},entries:function(){return u},get:function(e){return i[e.toLowerCase()]},has:function(e){return e.toLowerCase()in i}}}};for(var l in s.open(n.method||"get",e,!0),s.onload=function(){s.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,(function(e,n,t){o.push(n=n.toLowerCase()),u.push([n,t]),i[n]=i[n]?i[n]+","+t:t})),t(a())},s.onerror=r,s.withCredentials="include"==n.credentials,n.headers)s.setRequestHeader(l,n.headers[l]);s.send(n.body||null)}))}}}]);
--------------------------------------------------------------------------------
/storybook-dist/8.31b1fcf1df0801c904ff.manager.bundle.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /**
2 | * Prism: Lightweight, robust, elegant syntax highlighting
3 | *
4 | * @license MIT
5 | * @author Lea Verou
6 | * @namespace
7 | * @public
8 | */
9 |
--------------------------------------------------------------------------------
/storybook-dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thawkin3/js-data-structures-and-algorithms/14c5fde068f53ccaa10e04b1f22d802792720d04/storybook-dist/favicon.ico
--------------------------------------------------------------------------------
/storybook-dist/index.html:
--------------------------------------------------------------------------------
1 | Webpack App
--------------------------------------------------------------------------------
/storybook-dist/main.cb4f8469064561bafcc7.manager.bundle.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[1],{186:function(module,exports,__webpack_require__){__webpack_require__(187),__webpack_require__(386),module.exports=__webpack_require__(466)},386:function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__),__webpack_require__(77).a.setConfig({sidebar:{showRoots:!0}})},459:function(module,exports){}},[[186,2,3]]]);
--------------------------------------------------------------------------------
/storybook-dist/project.json:
--------------------------------------------------------------------------------
1 | {"generatedAt":1667070653379,"builder":{"name":"webpack4"},"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":false,"hasStorybookEslint":false,"refCount":0,"packageManager":{"type":"yarn","version":"1.22.4"},"storybookVersion":"6.5.13","language":"javascript","storybookPackages":{"@storybook/addons":{"version":"6.5.13"},"@storybook/react":{"version":"6.5.13"},"storybook-readme":{"version":"5.0.9"}},"framework":{"name":"react"},"addons":{}}
2 |
--------------------------------------------------------------------------------
/storybook-dist/runtime~main.5b3b37a5880e4df84c98.manager.bundle.js:
--------------------------------------------------------------------------------
1 | !function(modules){function webpackJsonpCallback(data){for(var moduleId,chunkId,chunkIds=data[0],moreModules=data[1],executeModules=data[2],i=0,resolves=[];i
11 | * @license MIT
12 | */
13 |
14 | /*!
15 | * https://github.com/es-shims/es5-shim
16 | * @license es5-shim Copyright 2009-2020 by contributors, MIT License
17 | * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
18 | */
19 |
20 | /*!
21 | * https://github.com/paulmillr/es6-shim
22 | * @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)
23 | * and contributors, MIT License
24 | * es6-shim: v0.35.4
25 | * see https://github.com/paulmillr/es6-shim/blob/0.35.3/LICENSE
26 | * Details and documentation:
27 | * https://github.com/paulmillr/es6-shim/
28 | */
29 |
30 | /*!
31 | * is-plain-object
32 | *
33 | * Copyright (c) 2014-2017, Jon Schlinkert.
34 | * Released under the MIT License.
35 | */
36 |
37 | /*!
38 | * isobject
39 | *
40 | * Copyright (c) 2014-2017, Jon Schlinkert.
41 | * Released under the MIT License.
42 | */
43 |
44 | /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */
45 |
46 | /**
47 | * Checks if an event is supported in the current execution environment.
48 | *
49 | * NOTE: This will not work correctly for non-generic events such as `change`,
50 | * `reset`, `load`, `error`, and `select`.
51 | *
52 | * Borrows from Modernizr.
53 | *
54 | * @param {string} eventNameSuffix Event name, e.g. "click".
55 | * @return {boolean} True if the event is supported.
56 | * @internal
57 | * @license Modernizr 3.0.0pre (Custom Build) | MIT
58 | */
59 |
60 | /**
61 | * Prism: Lightweight, robust, elegant syntax highlighting
62 | *
63 | * @license MIT
64 | * @author Lea Verou
65 | * @namespace
66 | * @public
67 | */
68 |
69 | /** @license React v0.20.2
70 | * scheduler-tracing.development.js
71 | *
72 | * Copyright (c) Facebook, Inc. and its affiliates.
73 | *
74 | * This source code is licensed under the MIT license found in the
75 | * LICENSE file in the root directory of this source tree.
76 | */
77 |
78 | /** @license React v0.20.2
79 | * scheduler.development.js
80 | *
81 | * Copyright (c) Facebook, Inc. and its affiliates.
82 | *
83 | * This source code is licensed under the MIT license found in the
84 | * LICENSE file in the root directory of this source tree.
85 | */
86 |
87 | /** @license React v16.13.1
88 | * react-is.development.js
89 | *
90 | * Copyright (c) Facebook, Inc. and its affiliates.
91 | *
92 | * This source code is licensed under the MIT license found in the
93 | * LICENSE file in the root directory of this source tree.
94 | */
95 |
96 | /** @license React v17.0.2
97 | * react-dom.development.js
98 | *
99 | * Copyright (c) Facebook, Inc. and its affiliates.
100 | *
101 | * This source code is licensed under the MIT license found in the
102 | * LICENSE file in the root directory of this source tree.
103 | */
104 |
105 | /** @license React v17.0.2
106 | * react-jsx-runtime.development.js
107 | *
108 | * Copyright (c) Facebook, Inc. and its affiliates.
109 | *
110 | * This source code is licensed under the MIT license found in the
111 | * LICENSE file in the root directory of this source tree.
112 | */
113 |
114 | /** @license React v17.0.2
115 | * react.development.js
116 | *
117 | * Copyright (c) Facebook, Inc. and its affiliates.
118 | *
119 | * This source code is licensed under the MIT license found in the
120 | * LICENSE file in the root directory of this source tree.
121 | */
122 |
123 | //! stable.js 0.1.8, https://github.com/Two-Screen/stable
124 |
125 | //! © 2018 Angry Bytes and contributors. MIT licensed.
126 |
--------------------------------------------------------------------------------
/storybook-dist/vendors~main.638b5667.iframe.bundle.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"vendors~main.638b5667.iframe.bundle.js","sources":[],"mappings":";A","sourceRoot":""}
--------------------------------------------------------------------------------
/storybook-dist/vendors~main.db18f16359f60198e93f.manager.bundle.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | object-assign
3 | (c) Sindre Sorhus
4 | @license MIT
5 | */
6 |
7 | /*!
8 | * Fuse.js v3.6.1 - Lightweight fuzzy-search (http://fusejs.io)
9 | *
10 | * Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me)
11 | * All Rights Reserved. Apache Software License 2.0
12 | *
13 | * http://www.apache.org/licenses/LICENSE-2.0
14 | */
15 |
16 | /*!
17 | * https://github.com/es-shims/es5-shim
18 | * @license es5-shim Copyright 2009-2020 by contributors, MIT License
19 | * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
20 | */
21 |
22 | /*!
23 | * https://github.com/paulmillr/es6-shim
24 | * @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)
25 | * and contributors, MIT License
26 | * es6-shim: v0.35.4
27 | * see https://github.com/paulmillr/es6-shim/blob/0.35.3/LICENSE
28 | * Details and documentation:
29 | * https://github.com/paulmillr/es6-shim/
30 | */
31 |
32 | /*!
33 | * isobject
34 | *
35 | * Copyright (c) 2014-2017, Jon Schlinkert.
36 | * Released under the MIT License.
37 | */
38 |
39 | /*! *****************************************************************************
40 | Copyright (c) Microsoft Corporation.
41 |
42 | Permission to use, copy, modify, and/or distribute this software for any
43 | purpose with or without fee is hereby granted.
44 |
45 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
46 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
47 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
48 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
49 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
50 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
51 | PERFORMANCE OF THIS SOFTWARE.
52 | ***************************************************************************** */
53 |
54 | /*! store2 - v2.13.1 - 2021-12-20
55 | * Copyright (c) 2021 Nathan Bubna; Licensed (MIT OR GPL-3.0) */
56 |
57 | /**
58 | * Checks if an event is supported in the current execution environment.
59 | *
60 | * NOTE: This will not work correctly for non-generic events such as `change`,
61 | * `reset`, `load`, `error`, and `select`.
62 | *
63 | * Borrows from Modernizr.
64 | *
65 | * @param {string} eventNameSuffix Event name, e.g. "click".
66 | * @return {boolean} True if the event is supported.
67 | * @internal
68 | * @license Modernizr 3.0.0pre (Custom Build) | MIT
69 | */
70 |
71 | /**
72 | * React Router DOM v6.0.2
73 | *
74 | * Copyright (c) Remix Software Inc.
75 | *
76 | * This source code is licensed under the MIT license found in the
77 | * LICENSE.md file in the root directory of this source tree.
78 | *
79 | * @license MIT
80 | */
81 |
82 | /**
83 | * React Router v6.0.2
84 | *
85 | * Copyright (c) Remix Software Inc.
86 | *
87 | * This source code is licensed under the MIT license found in the
88 | * LICENSE.md file in the root directory of this source tree.
89 | *
90 | * @license MIT
91 | */
92 |
93 | /** @license React v0.20.2
94 | * scheduler-tracing.development.js
95 | *
96 | * Copyright (c) Facebook, Inc. and its affiliates.
97 | *
98 | * This source code is licensed under the MIT license found in the
99 | * LICENSE file in the root directory of this source tree.
100 | */
101 |
102 | /** @license React v0.20.2
103 | * scheduler.development.js
104 | *
105 | * Copyright (c) Facebook, Inc. and its affiliates.
106 | *
107 | * This source code is licensed under the MIT license found in the
108 | * LICENSE file in the root directory of this source tree.
109 | */
110 |
111 | /** @license React v16.13.1
112 | * react-is.development.js
113 | *
114 | * Copyright (c) Facebook, Inc. and its affiliates.
115 | *
116 | * This source code is licensed under the MIT license found in the
117 | * LICENSE file in the root directory of this source tree.
118 | */
119 |
120 | /** @license React v17.0.2
121 | * react-dom.development.js
122 | *
123 | * Copyright (c) Facebook, Inc. and its affiliates.
124 | *
125 | * This source code is licensed under the MIT license found in the
126 | * LICENSE file in the root directory of this source tree.
127 | */
128 |
129 | /** @license React v17.0.2
130 | * react-is.development.js
131 | *
132 | * Copyright (c) Facebook, Inc. and its affiliates.
133 | *
134 | * This source code is licensed under the MIT license found in the
135 | * LICENSE file in the root directory of this source tree.
136 | */
137 |
138 | /** @license React v17.0.2
139 | * react.development.js
140 | *
141 | * Copyright (c) Facebook, Inc. and its affiliates.
142 | *
143 | * This source code is licensed under the MIT license found in the
144 | * LICENSE file in the root directory of this source tree.
145 | */
146 |
--------------------------------------------------------------------------------