chore: merge Fix/742 migrate doctest to jest (#749)

* Remove QuickSelect doctest

There are more Jest test cases already.

* Remove AverageMedian doctest

Already migrated to jest

* Migrate doctest for BinaryExponentiationRecursive.js

(also remove inline "main" test method)

* Migrate doctest for EulersTotient.js

(also remove inline "main" test method)

* Migrate doctest for PrimeFactors.js

(also remove inline "main" test method)

* Migrate doctest for BogoSort.js

Re-write prototype-polluting helper methods, too.

(also remove inline test driver code)

* Migrate doctest for BeadSort.js

(also remove inline test driver code)

* Migrate doctest for BucketSort.js

(also remove inline test driver code)

* Migrate doctest for CocktailShakerSort.js

(also remove inline test driver code)

* Migrate doctest for MergeSort.js

(also remove inline test driver code)

* Migrate doctest for QuickSort.js

(also remove inline test driver code)

* Migrate doctest for ReverseString.js

(also remove inline test driver code)

* Migrate doctest for ReverseString.js

* Migrate doctest for ValidateEmail.js

* Migrate doctest for ConwaysGameOfLife.js

(remove the animate code, too)

* Remove TernarySearch doctest

Already migrated to jest

* Migrate doctest for BubbleSort.js

(also remove inline test driver code)

* Remove doctest from CI and from dependencies

relates to #742
fixes #586

* Migrate doctest for RgbHsvConversion.js

* Add --fix option to "standard" npm script

* Migrate doctest for BreadthFirstSearch.js

(also remove inline test driver code)

* Migrate doctest for BreadthFirstShortestPath.js

(also remove inline test driver code)

* Migrate doctest for EulerMethod.js

(also remove inline test driver code)

Move manual test-code for plotting stuff in the browser in a distinct file, too. Those "*.manual-test.js" files are excluded from the UpdateDirectory.mjs script, as well.

* Migrate doctest for Mandelbrot.js

(also remove inline test driver code & moved manual drawing test into a *.manual-test.js)

* Migrate doctest for FloodFill.js

* Migrate doctest for KochSnowflake.js

(also move manual drawing test into a *.manual-test.js)

* Update npm lockfile

* Update README and COMMITTING with a few bits & bobs regarding testing & code quality
This commit is contained in:
Roland Hummel
2021-10-07 09:03:38 +02:00
committed by GitHub
parent 6eeb989930
commit b13b12e88c
53 changed files with 882 additions and 13514 deletions

View File

@ -76,7 +76,13 @@ function pathsToMarkdown (filePaths) {
}
// get paths of all .js files - excluding node_modules, the .github folder, tests and config stuff
globby(['**/*.js', '!(node_modules|.github)/**/*', '!**/*.test.js', '!babel.config.js'])
globby([
'**/*.js',
'!(node_modules|.github)/**/*',
'!**/*.test.js',
'!**/*.manual-test.js',
'!babel.config.js'
])
// create markdown content
.then(pathsToMarkdown)
// write markdown to file

View File

@ -15,9 +15,7 @@ jobs:
run: npm ci
- name: 🧪 Run tests
run: |
npm run doctest || true # TODO: Add all doctests
npm test
run: npm test
- name: 💄 Code style
run: npm run style

View File

@ -2,33 +2,39 @@
## Before contributing
Welcome to [TheAlgorithms/Javascript](https://github.com/TheAlgorithms/Javascript)! Before sending your pull requests, make sure that you **read the whole guidelines**. If you have any doubt on the contributing guide, please feel free to [state it clearly in an issue](https://github.com/TheAlgorithms/Javascript/issues/new)
Welcome to [TheAlgorithms/Javascript](https://github.com/TheAlgorithms/Javascript)! Before sending your pull requests,
make sure that you **read the whole guidelines**. If you have any doubt on the contributing guide, please feel free to
[state it clearly in an issue](https://github.com/TheAlgorithms/Javascript/issues/new).
## Contributing
### Contributor
We are very happy that you consider implementing algorithms and data structures for others! This repository is referenced and used by learners from around the globe. Being one of our contributors, you agree and confirm that:
We are very happy that you consider implementing algorithms and data structures for others! This repository is
referenced and used by learners from around the globe. Being one of our contributors, you agree and confirm that:
- You did your work - plagiarism is not allowed.
- Any plagiarized work will not be merged.
- Your work will be distributed under [GNU License](LICENSE) once your pull request is merged
- Your submitted work must fulfill our styles and standards
* You did your work - plagiarism is not allowed.
* Any plagiarized work will not be merged.
* Your work will be distributed under [GNU License](LICENSE) once your pull request is merged.
* Your submitted work must fulfill our styles and standards.
**New implementation** is welcome! For example, new solutions to a problem, different representations of a graph data structure or algorithm designs with different complexity.
**New implementation** is welcome! For example, new solutions to a problem, different representations of a graph data
structure or algorithm designs with different complexity.
**Improving comments** and **writing proper tests** are also highly welcome.
### Contribution
We appreciate any contribution, from fixing grammar mistakes to implementing complex algorithms. Please read this section if you are contributing to your work.
We appreciate any contribution, from fixing grammar mistakes to implementing complex algorithms. Please read this
section if you are contributing to your work.
If you submit a pull request that resolves an open issue, please help us to keep our issue list small by adding `fixes: #{$ISSUE_NO}` to your commit message. GitHub will use this tag to auto-close the issue if your PR is merged.
If you submit a pull request that resolves an open issue, please help us to keep our issue list small by adding
`fixes: #{$ISSUE_NO}` to your commit message. GitHub will use this tag to auto-close the issue if your PR is merged.
#### What is an Algorithm?
An Algorithm is one or more functions (or classes) that:
* take one or more inputs,
* perform some internal calculations or data manipulations,
* return one or more outputs,
@ -37,66 +43,97 @@ An Algorithm is one or more functions (or classes) that:
Algorithms should be packaged in a way that would make it easy for readers to put them into larger programs.
Algorithms should:
* have intuitive class and function names that make their purpose clear to readers
* use JavaScript naming conventions and intuitive variable names to ease comprehension
* be flexible to take different input values
* raise JavaScript exceptions (RangeError, etc.) on erroneous input values
Algorithms in this repo should not be how-to examples for existing JavaScript packages. Instead, they should perform internal calculations or manipulations to convert input values into different output values. Those calculations or manipulations can use data types, classes, or functions of existing JavaScript packages but each algorithm in this repo should add unique value.
Algorithms in this repo should not be how-to examples for existing JavaScript packages. Instead, they should perform
internal calculations or manipulations to convert input values into different output values. Those calculations or
manipulations can use data types, classes, or functions of existing JavaScript packages but each algorithm in this repo
should add unique value.
#### File Naming Convention
- filenames should use the UpperCamelCase (PascalCase) style.
- There should be no spaces in filenames.
**Example:**`UserProfile.js` is allowed but `userprofile.js`,`Userprofile.js`,`user-Profile.js`,`userProfile.js` are not
* filenames should use the UpperCamelCase (PascalCase) style.
* There should be no spaces in filenames.
* **Example:**`UserProfile.js` is allowed but `userprofile.js`,`Userprofile.js`,`user-Profile.js`,`userProfile.js` are
not.
#### Testing
Be confident that your code works. When was the last time you committed a code change, your build failed, and half of your app stopped working? Mine was last week. Writing tests for our Algorithms will help us ensure the implementations are air tight even after multiple fixes and code changes.
We use a NPM package [doctest](https://www.npmjs.com/package/doctest) to add basic testing functionality to our code. Doctests are simple structured comments that evaluate a function and ensure a required result.
Be confident that your code works. When was the last time you committed a code change, your build failed, and half of
your app stopped working? Mine was last week. Writing tests for our Algorithms will help us ensure the implementations
are air tight even after multiple fixes and code changes.
The implementation of doctest is quite simple. You can easily learn it [here](https://www.npmjs.com/package/doctest).
We use [Jest](https://jestjs.io/) to run unit tests on our algorithms. It provides a very readable and expressive way to
structure your test code.
It is advised that you add the Doctests in a multiline comment just after the Algorithm description.
For Code Structure reference see [this file](https://github.com/TheAlgorithms/Javascript/blob/master/Sorts/BubbleSort.js).
It is advised that the algorithm file (module) does not contain any "live" code but rather just exports the function(s)
needed to execute the algorithm. Your test code can import those function(s), call them with the appropriate parameters
and inspect the outcome. Example: [RatInAMaze.test.js](Backtracking/tests/RatInAMaze.test.js).
You can run the doctest by using the command
Please refrain from using `console` in your implementation AND test code.
You can (and should!) run all tests locally before committing your changes:
```shell
npm test
```
$ doctest MyFile.js // if that fails, try: npx doctest MyFile.js
If you want save some time and just run a specific test:
```shell
# this will run any test file where the filename matches "koch"
npm test -- koch
```
You can also start Jest in "watch" mode:
```shell
npm test -- --watchAll
```
This will run all tests and watch source and test files for changes. When a change is made, the tests will run again.
#### Coding Style
To maximize the readability and correctness of our code, we require that new submissions follow [JavaScript Standard Style](https://standardjs.com/)
- Command to install JavaScript Standard Style
```
$ npm install standard --save-dev
```
- Usage
```
$ standard MyFile.js // if that fails, try: npx standard MyFile.js
```
To maximize the readability and correctness of our code, we require that new submissions follow the
[JavaScript Standard Style](https://standardjs.com/).
- Use camelCase with the leading character as lowercase for identifier names (variables and functions)
- Names start with a letter
- follow code indentation
- Always use 2 spaces for indentation of code blocks
```
function sumOfArray (arrayOfNumbers) {
let sum = 0
for (let i = 0; i < arrayOfNumbers.length; i++) {
sum += arrayOfNumbers[i]
}
return (sum)
}
Before committing, please run
```
- Avoid using global variables and avoid '=='
- Please use 'let' over 'var'
- Please use 'console.log()'
- We strongly recommend the use of ECMAScript 6
- Avoid importing external libraries for basic algorithms. Only use those libraries for complicated algorithms.
- Most importantly,
- **Be consistent in the use of these guidelines when submitting.**
- Happy coding!
```shell
npm run style
```
in order to apply the coding style (where it can be done automatically). If an error is shown, please figure out what's
wrong, fix it and run standard again.
A few (but not all) of the things to keep in mind:
* Use camelCase with the leading character as lowercase for identifier names (variables and functions)
* Names start with a letter
* Follow code indentation: Always use 2 spaces for indentation of code blocks
```js
function sumOfArray (arrayOfNumbers) {
let sum = 0
for (let i = 0; i < arrayOfNumbers.length; i++) {
sum += arrayOfNumbers[i]
}
return (sum)
}
```
*
* Avoid using global variables and avoid `==`
* Please use `let` over `var`
* Please refrain from using `console.log` or any other console methods
* **Absolutely** don't use `alert`
* We strongly recommend the use of ECMAScript 6
* Avoid importing external libraries for basic algorithms. Only use those libraries for complicated algorithms
* Most importantly:
* **Be consistent in the use of these guidelines when submitting**
* Happy coding!
Writer [@itsvinayak](https://github.com/itsvinayak), May 2020.

View File

@ -8,17 +8,10 @@ The Game of Life is a cellular automaton devised by the British mathematician Jo
(example adapted from https://github.com/TheAlgorithms/Python/blob/master/cellular_automata/conways_game_of_life.py )
*/
/*
* Doctests
*
* > newGeneration([[0, 1, 0], [0, 1, 0], [0, 1, 0]])
* [ [ 0, 0, 0 ], [ 1, 1, 1 ], [ 0, 0, 0 ] ]
*/
/*
* Generates the next generation for a given state of Conway's Game of Life.
*/
function newGeneration (cells) {
/**
* Generates the next generation for a given state of Conway's Game of Life.
*/
export function newGeneration (cells) {
const nextGeneration = []
for (let i = 0; i < cells.length; i++) {
const nextGenerationRow = []
@ -46,45 +39,3 @@ function newGeneration (cells) {
}
return nextGeneration
}
/*
* utility function to display a series of generations in the console
*/
async function animate (cells, steps) {
/*
* utility function to print one frame
*/
function printCells (cells) {
console.clear()
for (let i = 0; i < cells.length; i++) {
let line = ''
for (let j = 0; j < cells[i].length; j++) {
if (cells[i][j] === 1) line += '\u2022'
else line += ' '
}
console.log(line)
}
}
printCells(cells)
for (let i = 0; i < steps; i++) {
await new Promise(resolve => setTimeout(resolve, 250)) // sleep
cells = newGeneration(cells)
printCells(cells)
}
}
// Define glider example
const glider = [
[0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]
]
animate(glider, 16)

View File

@ -0,0 +1,8 @@
import { newGeneration } from './ConwaysGameOfLife'
describe('newGeneration', () => {
it('should produce the next generation according to the rules', () => {
expect(newGeneration([[0, 1, 0], [0, 1, 0], [0, 1, 0]]))
.toEqual([[0, 0, 0], [1, 1, 1], [0, 0, 0]])
})
})

View File

@ -1,4 +1,4 @@
/**
/*
* The RGB color model is an additive color model in which red, green, and blue light are added
* together in various ways to reproduce a broad array of colors. The name of the model comes from
* the initials of the three additive primary colors, red, green, and blue. Meanwhile, the HSV
@ -8,57 +8,6 @@
* https://en.wikipedia.org/wiki/RGB_color_model and https://en.wikipedia.org/wiki/HSL_and_HSV).
*/
/*
Doctests
Expected RGB-values taken from https://www.rapidtables.com/convert/color/hsv-to-rgb.html
Test hsvToRgb-method
> hsvToRgb(0, 0, 0)
[0, 0, 0]
> hsvToRgb(0, 0, 1)
[255, 255, 255]
> hsvToRgb(0, 1, 1)
[255, 0, 0]
> hsvToRgb(60, 1, 1)
[255, 255, 0]
> hsvToRgb(120, 1, 1)
[0, 255, 0]
> hsvToRgb(240, 1, 1)
[0, 0, 255]
> hsvToRgb(300, 1, 1)
[255, 0, 255]
> hsvToRgb(180, 0.5, 0.5)
[64, 128, 128]
> hsvToRgb(234, 0.14, 0.88)
[193, 196, 224]
> hsvToRgb(330, 0.75, 0.5)
[128, 32, 80]
Test rgbToHsv-method
function "approximatelyEqualHsv" needed because of small deviations due to rounding for the RGB-values.
> approximatelyEqualHsv(rgbToHsv(0, 0, 0), [0, 0, 0])
true
> approximatelyEqualHsv(rgbToHsv(255, 255, 255), [0, 0, 1])
true
> approximatelyEqualHsv(rgbToHsv(255, 0, 0), [0, 1, 1])
true
> approximatelyEqualHsv(rgbToHsv(255, 255, 0), [60, 1, 1])
true
> approximatelyEqualHsv(rgbToHsv(0, 255, 0), [120, 1, 1])
true
> approximatelyEqualHsv(rgbToHsv(0, 0, 255), [240, 1, 1])
true
> approximatelyEqualHsv(rgbToHsv(255, 0, 255), [300, 1, 1])
true
> approximatelyEqualHsv(rgbToHsv(64, 128, 128), [180, 0.5, 0.5])
true
> approximatelyEqualHsv(rgbToHsv(193, 196, 224), [234, 0.14, 0.88])
true
> approximatelyEqualHsv(rgbToHsv(128, 32, 80), [330, 0.75, 0.5])
true
*/
/**
* Conversion from the HSV-representation to the RGB-representation.
*
@ -67,7 +16,7 @@ true
* @param value Brightness-value of the color.
* @return The tuple of RGB-components.
*/
function hsvToRgb (hue, saturation, value) { // eslint-disable-line no-unused-vars
export function hsvToRgb (hue, saturation, value) {
if (hue < 0 || hue > 360) {
throw new Error('hue should be between 0 and 360')
}
@ -96,7 +45,7 @@ function hsvToRgb (hue, saturation, value) { // eslint-disable-line no-unused-va
* @param blue Blue-component of the color.
* @return The tuple of HSV-components.
*/
function rgbToHsv (red, green, blue) { // eslint-disable-line no-unused-vars
export function rgbToHsv (red, green, blue) {
if (red < 0 || red > 255) {
throw new Error('red should be between 0 and 255')
}
@ -120,7 +69,7 @@ function rgbToHsv (red, green, blue) { // eslint-disable-line no-unused-vars
if (chroma === 0) {
hue = 0
} else if (value === dRed) {
hue = 60 * (0 + (dGreen - dBlue) / chroma)
hue = 60 * ((dGreen - dBlue) / chroma)
} else if (value === dGreen) {
hue = 60 * (2 + (dBlue - dRed) / chroma)
} else {
@ -132,7 +81,7 @@ function rgbToHsv (red, green, blue) { // eslint-disable-line no-unused-vars
return [hue, saturation, value]
}
function approximatelyEqualHsv (hsv1, hsv2) { // eslint-disable-line no-unused-vars
export function approximatelyEqualHsv (hsv1, hsv2) {
const bHue = Math.abs(hsv1[0] - hsv2[0]) < 0.2
const bSaturation = Math.abs(hsv1[1] - hsv2[1]) < 0.002
const bValue = Math.abs(hsv1[2] - hsv2[2]) < 0.002
@ -140,8 +89,7 @@ function approximatelyEqualHsv (hsv1, hsv2) { // eslint-disable-line no-unused-v
return bHue && bSaturation && bValue
}
function getRgbBySection (
hueSection, chroma, matchValue, secondLargestComponent) {
function getRgbBySection (hueSection, chroma, matchValue, secondLargestComponent) {
function convertToInt (input) {
return Math.round(255 * input)
}

View File

@ -0,0 +1,33 @@
import { approximatelyEqualHsv, hsvToRgb, rgbToHsv } from '../RgbHsvConversion'
describe('hsvToRgb', () => {
// Expected RGB-values taken from https://www.rapidtables.com/convert/color/hsv-to-rgb.html
it('should calculate the correct RGB values', () => {
expect(hsvToRgb(0, 0, 0)).toEqual([0, 0, 0])
expect(hsvToRgb(0, 0, 1)).toEqual([255, 255, 255])
expect(hsvToRgb(0, 1, 1)).toEqual([255, 0, 0])
expect(hsvToRgb(60, 1, 1)).toEqual([255, 255, 0])
expect(hsvToRgb(120, 1, 1)).toEqual([0, 255, 0])
expect(hsvToRgb(240, 1, 1)).toEqual([0, 0, 255])
expect(hsvToRgb(300, 1, 1)).toEqual([255, 0, 255])
expect(hsvToRgb(180, 0.5, 0.5)).toEqual([64, 128, 128])
expect(hsvToRgb(234, 0.14, 0.88)).toEqual([193, 196, 224])
expect(hsvToRgb(330, 0.75, 0.5)).toEqual([128, 32, 80])
})
})
describe('rgbToHsv', () => {
// "approximatelyEqualHsv" needed because of small deviations due to rounding for the RGB-values
it('should calculate the correct HSV values', () => {
expect(approximatelyEqualHsv(rgbToHsv(0, 0, 0), [0, 0, 0])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(255, 255, 255), [0, 0, 1])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(255, 0, 0), [0, 1, 1])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(255, 255, 0), [60, 1, 1])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(0, 255, 0), [120, 1, 1])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(0, 0, 255), [240, 1, 1])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(255, 0, 255), [300, 1, 1])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(64, 128, 128), [180, 0.5, 0.5])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(193, 196, 224), [234, 0.14, 0.88])).toEqual(true)
expect(approximatelyEqualHsv(rgbToHsv(128, 32, 80), [330, 0.75, 0.5])).toEqual(true)
})
})

View File

@ -61,15 +61,4 @@ function Swap (arr, x, y) {
[arr[x], arr[y]] = [arr[y], arr[x]]
}
// > QuickSelect([1, 4, 2, -2, 4, 5], 1)
// -2
// > QuickSelect([1, 4, 2, -2, 4, 5], 5)
// 4
// > QuickSelect([1, 4, 2, -2, 4, 5], 6)
// 5
// > QuickSelect([1, 4, 2, -2, 4, 5], 0)
// "Index Out of Bound"
// > QuickSelect([1, 4, 2, -2, 4, 5], 7)
// "Index Out of Bound"
export { QuickSelect }

View File

@ -1,20 +1,13 @@
/*
Breadth-first search is an algorithm for traversing a graph. It's discovers all nodes reachable from the starting position by exploring all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.
(description adapted from https://en.wikipedia.org/wiki/Breadth-first_search )
(see also: https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core )
*/
/*
Doctests
> Array.from(breadthFirstSearch(graph, "C"))
[ 'C', 'D', 'A', 'B', 'E' ]
> Array.from(breadthFirstSearch(graph, "A"))
[ 'A', 'B', 'D', 'E' ]
> Array.from(breadthFirstSearch(graph, "F"))
[ 'F', 'G' ]
*/
function breadthFirstSearch (graph, startingNode) {
/**
* Breadth-first search is an algorithm for traversing a graph.
*
* It discovers all nodes reachable from the starting position by exploring all of the neighbor nodes at the present
* depth prior to moving on to the nodes at the next depth level.
*
* (description adapted from https://en.wikipedia.org/wiki/Breadth-first_search)
* @see https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core
*/
export function breadthFirstSearch (graph, startingNode) {
// visited keeps track of all nodes visited
const visited = new Set()
@ -39,26 +32,3 @@ function breadthFirstSearch (graph, startingNode) {
return visited
}
const graph = {
A: ['B', 'D'],
B: ['E'],
C: ['D'],
D: ['A'],
E: ['D'],
F: ['G'],
G: []
}
/*
A <-> B
ʌ |
| |
v v
C --> D <-- E
F --> G
*/
console.log(breadthFirstSearch(graph, 'C'))
console.log(breadthFirstSearch(graph, 'A'))
console.log(breadthFirstSearch(graph, 'F'))

View File

@ -1,25 +1,13 @@
/*
Breadth-first approach can be applied to determine the shortest path between two nodes
in an equi-weighted graph. It searches the target node among all neighbors of the
starting node, then the process is repeated on the level of the neighbors of the
neighbors and so on.
(See also: https://en.wikipedia.org/wiki/Breadth-first_search )
(see also: https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core )
*/
/*
Doctests
> breadthFirstShortestPath(graph, 'C', 'E')
[ 'C', 'D', 'A', 'B', 'E' ]
> breadthFirstShortestPath(graph, 'E', 'B')
[ 'E', 'D', 'A', 'B' ]
> breadthFirstShortestPath(graph, 'F', 'G')
[ 'F', 'G' ]
> breadthFirstShortestPath(graph, 'A', 'G')
[]
*/
function breadthFirstShortestPath (graph, startNode, targetNode) {
/**
* Breadth-first approach can be applied to determine the shortest path between two nodes in an equi-weighted graph.
*
* It searches the target node among all neighbors of the starting node, then the process is repeated on the level of
* the neighbors of the neighbors and so on.
*
* @see https://en.wikipedia.org/wiki/Breadth-first_search
* @see https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core
*/
export function breadthFirstShortestPath (graph, startNode, targetNode) {
// check if startNode & targetNode are identical
if (startNode === targetNode) {
return [startNode]
@ -62,27 +50,3 @@ function breadthFirstShortestPath (graph, startNode, targetNode) {
// the target node was not reachable
return []
}
const graph = {
A: ['B', 'D'],
B: ['E'],
C: ['D'],
D: ['A'],
E: ['D'],
F: ['G'],
G: []
}
/*
A <-> B
ʌ |
| |
v v
C --> D <-- E
F --> G
*/
console.log(breadthFirstShortestPath(graph, 'C', 'E'))
console.log(breadthFirstShortestPath(graph, 'E', 'B'))
console.log(breadthFirstShortestPath(graph, 'F', 'G'))
console.log(breadthFirstShortestPath(graph, 'A', 'G'))

View File

@ -0,0 +1,28 @@
import { breadthFirstSearch } from '../BreadthFirstSearch'
describe('BreadthFirstSearch', () => {
const graph = {
A: ['B', 'D'],
B: ['E'],
C: ['D'],
D: ['A'],
E: ['D'],
F: ['G'],
G: []
}
/*
A <-> B
ʌ |
| |
v v
C --> D <-- E
F --> G
*/
it('should return the visited nodes', () => {
expect(Array.from(breadthFirstSearch(graph, 'C'))).toEqual(['C', 'D', 'A', 'B', 'E'])
expect(Array.from(breadthFirstSearch(graph, 'A'))).toEqual(['A', 'B', 'D', 'E'])
expect(Array.from(breadthFirstSearch(graph, 'F'))).toEqual(['F', 'G'])
})
})

View File

@ -0,0 +1,29 @@
import { breadthFirstShortestPath } from '../BreadthFirstShortestPath'
describe('BreadthFirstShortestPath', () => {
const graph = {
A: ['B', 'D'],
B: ['E'],
C: ['D'],
D: ['A'],
E: ['D'],
F: ['G'],
G: []
}
/*
A <-> B
ʌ |
| |
v v
C --> D <-- E
F --> G
*/
it('should return the visited nodes', () => {
expect(breadthFirstShortestPath(graph, 'C', 'E')).toEqual(['C', 'D', 'A', 'B', 'E'])
expect(breadthFirstShortestPath(graph, 'E', 'B')).toEqual(['E', 'D', 'A', 'B'])
expect(breadthFirstShortestPath(graph, 'F', 'G')).toEqual(['F', 'G'])
expect(breadthFirstShortestPath(graph, 'A', 'G')).toEqual([])
})
})

View File

@ -8,19 +8,9 @@
* else if the length of the array is odd number, the median value will be the middle number in the array
*/
/*
* Doctests
*
* > averageMedian([8, 9, 1, 2, 5, 10, 11])
* 8
* > averageMedian([15, 18, 3, 9, 13, 5])
* 11
* > averageMedian([1,2,3,4,6,8])
* 3.5
*/
const averageMedian = (numbers) => {
let median = 0; const numLength = numbers.length
let median = 0
const numLength = numbers.length
numbers = numbers.sort(sortNumbers)
if (numLength % 2 === 0) {

View File

@ -6,7 +6,7 @@
https://en.wikipedia.org/wiki/Exponentiation_by_squaring
*/
const binaryExponentiation = (a, n) => {
export const binaryExponentiation = (a, n) => {
// input: a: int, n: int
// returns: a^n: int
if (n === 0) {
@ -18,14 +18,3 @@ const binaryExponentiation = (a, n) => {
return b * b
}
}
const main = () => {
// binary_exponentiation(2, 10)
// > 1024
console.log(binaryExponentiation(2, 10))
// binary_exponentiation(3, 9)
// > 19683
console.log(binaryExponentiation(3, 9))
}
main()

View File

@ -1,28 +1,19 @@
/*
In mathematics and computational science, the Euler method (also called forward Euler method) is a first-order numerical procedure for solving ordinary differential equations (ODEs) with a given initial value. It is the most basic explicit method for numerical integration of ordinary differential equations. The method proceeds in a series of steps. At each step the y-value is calculated by evaluating the differential equation at the previous step, multiplying the result with the step-size and adding it to the last y-value: y_n+1 = y_n + stepSize * f(x_n, y_n).
(description adapted from https://en.wikipedia.org/wiki/Euler_method )
(see also: https://www.geeksforgeeks.org/euler-method-solving-differential-equation/ )
*/
/*
Doctests
> eulerStep(0, 0.1, 0, function(x, y){return x})
0
> eulerStep(2, 1, 1, function(x, y){return x * x})
5
> eulerFull(0, 3, 1, 0, function(x, y){return x})
[{"x": 0, "y": 0}, {"x": 1, "y": 0}, {"x": 2, "y": 1}, {"x": 3, "y": 3}]
> eulerFull(3, 4, 0.5, 1, function(x, y){return x * x})
[{"x": 3, "y": 1}, {"x": 3.5, "y": 5.5}, {"x": 4, "y": 11.625}]
*/
function eulerStep (xCurrent, stepSize, yCurrent, differentialEquation) {
/**
* In mathematics and computational science, the Euler method (also called forward Euler method) is a first-order
* numerical procedure for solving ordinary differential equations (ODEs) with a given initial value. It is the most
* basic explicit method for numerical integration of ordinary differential equations. The method proceeds in a series
* of steps. At each step the y-value is calculated by evaluating the differential equation at the previous step,
* multiplying the result with the step-size and adding it to the last y-value: y_n+1 = y_n + stepSize * f(x_n, y_n).
*
* (description adapted from https://en.wikipedia.org/wiki/Euler_method)
* @see https://www.geeksforgeeks.org/euler-method-solving-differential-equation/
*/
export function eulerStep (xCurrent, stepSize, yCurrent, differentialEquation) {
// calculates the next y-value based on the current value of x, y and the stepSize
const yNext = yCurrent + stepSize * differentialEquation(xCurrent, yCurrent)
return yNext
return yCurrent + stepSize * differentialEquation(xCurrent, yCurrent)
}
function eulerFull (xStart, xEnd, stepSize, yStart, differentialEquation) {
export function eulerFull (xStart, xEnd, stepSize, yStart, differentialEquation) {
// loops through all the steps until xEnd is reached, adds a point for each step and then returns all the points
const points = [{ x: xStart, y: yStart }]
let yCurrent = yStart
@ -37,72 +28,3 @@ function eulerFull (xStart, xEnd, stepSize, yStart, differentialEquation) {
return points
}
function plotLine (label, points, width, height) {
// utility function to plot the results
// container needed to control the size of the canvas
const container = document.createElement('div')
container.style.width = width + 'px'
container.style.height = height + 'px'
document.body.append(container)
// the canvas for plotting
const canvas = document.createElement('canvas')
container.append(canvas)
// Chart-class from chartjs
const chart = new Chart(canvas, { // eslint-disable-line
type: 'scatter',
data: {
datasets: [{
label: label,
data: points,
showLine: true,
fill: false,
tension: 0,
borderColor: 'black'
}]
},
options: {
maintainAspectRatio: false,
responsive: true
}
})
}
function exampleEquation1 (x, y) {
return x
}
// example from https://en.wikipedia.org/wiki/Euler_method
function exampleEquation2 (x, y) {
return y
}
// example from https://www.geeksforgeeks.org/euler-method-solving-differential-equation/
function exampleEquation3 (x, y) {
return x + y + x * y
}
const points1 = eulerFull(0, 4, 0.1, 0, exampleEquation1)
const points2 = eulerFull(0, 4, 0.1, 1, exampleEquation2)
const points3 = eulerFull(0, 0.1, 0.025, 1, exampleEquation3)
console.log(points1)
console.log(points2)
console.log(points3)
// plot the results if the script is executed in a browser with a window-object
if (typeof window !== 'undefined') {
const script = document.createElement('script')
// using chartjs
script.src = 'https://www.chartjs.org/dist/2.9.4/Chart.min.js'
script.onload = function () {
plotLine('example 1: dy/dx = x', points1, 600, 400)
plotLine('example 2: dy/dx = y', points2, 600, 400)
plotLine('example 3: dy/dx = x + y + x * y', points3, 600, 400)
}
document.body.append(script)
}

View File

@ -8,7 +8,7 @@
O(sqrt(n))
*/
const EulersTotient = (n) => {
export const EulersTotient = (n) => {
// input: n: int
// output: phi(n): count of numbers b/w 1 and n that are coprime to n
let res = n
@ -27,14 +27,3 @@ const EulersTotient = (n) => {
}
return res
}
const main = () => {
// EulersTotient(9) = 6 as 1, 2, 4, 5, 7, and 8 are coprime to 9
// > 6
console.log(EulersTotient(9))
// EulersTotient(10) = 4 as 1, 3, 7, 9 are coprime to 10
// > 4
console.log(EulersTotient(10))
}
main()

View File

@ -1,43 +1,22 @@
/**
* The Mandelbrot set is the set of complex numbers "c" for which the series "z_(n+1) = z_n * z_n +
* c" does not diverge, i.e. remains bounded. Thus, a complex number "c" is a member of the
* Mandelbrot set if, when starting with "z_0 = 0" and applying the iteration repeatedly, the
* absolute value of "z_n" remains bounded for all "n > 0". Complex numbers can be written as "a +
* b*i": "a" is the real component, usually drawn on the x-axis, and "b*i" is the imaginary
* component, usually drawn on the y-axis. Most visualizations of the Mandelbrot set use a
* color-coding to indicate after how many steps in the series the numbers outside the set cross the
* divergence threshold. Images of the Mandelbrot set exhibit an elaborate and infinitely
* complicated boundary that reveals progressively ever-finer recursive detail at increasing
* magnifications, making the boundary of the Mandelbrot set a fractal curve. (description adapted
* from https://en.wikipedia.org/wiki/Mandelbrot_set ) (see also
* https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set )
*/
/*
Doctests
Test black and white
Pixel outside the Mandelbrot set should be white.
Pixel inside the Mandelbrot set should be black.
> getRGBData(800, 600, -0.6, 0, 3.2, 50, false)[0][0]
[255, 255, 255]
> getRGBData(800, 600, -0.6, 0, 3.2, 50, false)[400][300]
[0, 0, 0]
Test color-coding
Pixel distant to the Mandelbrot set should be red.
Pixel inside the Mandelbrot set should be black.
> getRGBData(800, 600, -0.6, 0, 3.2, 50, true)[0][0]
[255, 0, 0]
> getRGBData(800, 600, -0.6, 0, 3.2, 50, true)[400][300]
[0, 0, 0]
*/
/**
* Method to generate the image of the Mandelbrot set. Two types of coordinates are used:
* image-coordinates that refer to the pixels and figure-coordinates that refer to the complex
* numbers inside and outside the Mandelbrot set. The figure-coordinates in the arguments of this
* method determine which section of the Mandelbrot set is viewed. The main area of the Mandelbrot
* set is roughly between "-1.5 < x < 0.5" and "-1 < y < 1" in the figure-coordinates.
* Method to generate the image of the Mandelbrot set.
*
* Two types of coordinates are used: image-coordinates that refer to the pixels and figure-coordinates that refer to
* the complex numbers inside and outside the Mandelbrot set. The figure-coordinates in the arguments of this method
* determine which section of the Mandelbrot set is viewed. The main area of the Mandelbrot set is roughly between
* "-1.5 < x < 0.5" and "-1 < y < 1" in the figure-coordinates.
*
* The Mandelbrot set is the set of complex numbers "c" for which the series "z_(n+1) = z_n * z_n + c" does not diverge,
* i.e. remains bounded. Thus, a complex number "c" is a member of the Mandelbrot set if, when starting with "z_0 = 0"
* and applying the iteration repeatedly, the absolute value of "z_n" remains bounded for all "n > 0". Complex numbers
* can be written as "a + b*i": "a" is the real component, usually drawn on the x-axis, and "b*i" is the imaginary
* component, usually drawn on the y-axis. Most visualizations of the Mandelbrot set use a color-coding to indicate
* after how many steps in the series the numbers outside the set cross the divergence threshold. Images of the
* Mandelbrot set exhibit an elaborate and infinitely complicated boundary that reveals progressively ever-finer
* recursive detail at increasing magnifications, making the boundary of the Mandelbrot set a fractal curve.
*
* (description adapted from https://en.wikipedia.org/wiki/Mandelbrot_set)
* @see https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set
*
* @param {number} imageWidth The width of the rendered image.
* @param {number} imageHeight The height of the rendered image.
@ -45,10 +24,10 @@ Pixel inside the Mandelbrot set should be black.
* @param {number} figureCenterY The y-coordinate of the center of the figure.
* @param {number} figureWidth The width of the figure.
* @param {number} maxStep Maximum number of steps to check for divergent behavior.
* @param {number} useDistanceColorCoding Render in color or black and white.
* @param {boolean} useDistanceColorCoding Render in color or black and white.
* @return {object} The RGB-data of the rendered Mandelbrot set.
*/
function getRGBData (
export function getRGBData (
imageWidth = 800,
imageHeight = 600,
figureCenterX = -0.6,
@ -83,9 +62,9 @@ function getRGBData (
// color the corresponding pixel based on the selected coloring-function
rgbData[imageX][imageY] =
useDistanceColorCoding
? colorCodedColorMap(distance)
: blackAndWhiteColorMap(distance)
useDistanceColorCoding
? colorCodedColorMap(distance)
: blackAndWhiteColorMap(distance)
}
}
@ -93,8 +72,9 @@ function getRGBData (
}
/**
* Black and white color-coding that ignores the relative distance. The Mandelbrot set is black,
* everything else is white.
* Black and white color-coding that ignores the relative distance.
*
* The Mandelbrot set is black, everything else is white.
*
* @param {number} distance Distance until divergence threshold
* @return {object} The RGB-value corresponding to the distance.
@ -104,7 +84,9 @@ function blackAndWhiteColorMap (distance) {
}
/**
* Color-coding taking the relative distance into account. The Mandelbrot set is black.
* Color-coding taking the relative distance into account.
*
* The Mandelbrot set is black.
*
* @param {number} distance Distance until divergence threshold
* @return {object} The RGB-value corresponding to the distance.
@ -145,11 +127,12 @@ function colorCodedColorMap (distance) {
/**
* Return the relative distance (ratio of steps taken to maxStep) after which the complex number
* constituted by this x-y-pair diverges. Members of the Mandelbrot set do not diverge so their
* distance is 1.
* constituted by this x-y-pair diverges.
*
* Members of the Mandelbrot set do not diverge so their distance is 1.
*
* @param {number} figureX The x-coordinate within the figure.
* @param {number} figureX The y-coordinate within the figure.
* @param {number} figureY The y-coordinate within the figure.
* @param {number} maxStep Maximum number of steps to check for divergent behavior.
* @return {number} The relative distance as the ratio of steps taken to maxStep.
*/
@ -171,22 +154,3 @@ function getDistance (figureX, figureY, maxStep) {
}
return currentStep / (maxStep - 1)
}
// plot the results if the script is executed in a browser with a window-object
if (typeof window !== 'undefined') {
const rgbData = getRGBData()
const width = rgbData.length
const height = rgbData[0].length
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
const ctx = canvas.getContext('2d')
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const rgb = rgbData[x][y]
ctx.fillStyle = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'
ctx.fillRect(x, y, 1, 1)
}
}
document.body.append(canvas)
}

View File

@ -3,7 +3,7 @@
https://github.com/TheAlgorithms/Python/blob/master/maths/prime_factors.py
*/
const PrimeFactors = (n) => {
export const PrimeFactors = (n) => {
// input: n: int
// output: primeFactors: Array of all prime factors of n
const primeFactors = []
@ -20,14 +20,3 @@ const PrimeFactors = (n) => {
}
return primeFactors
}
const main = () => {
// PrimeFactors(100)
// > [ 2, 2, 5, 5 ]
console.log(PrimeFactors(100))
// PrimeFactors(2560)
// > [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 5 ]
console.log(PrimeFactors(2560))
}
main()

View File

@ -0,0 +1,11 @@
const { binaryExponentiation } = require('../BinaryExponentiationRecursive')
describe('BinaryExponentiationRecursive', () => {
it('should calculate 2 to the power of 10 correctly', () => {
expect(binaryExponentiation(2, 10)).toBe(1024)
})
it('should calculate 3 to the power of 9 correctly', () => {
expect(binaryExponentiation(3, 9)).toBe(19683)
})
})

View File

@ -0,0 +1,66 @@
import { eulerFull } from '../EulerMethod'
function plotLine (label, points, width, height) {
// utility function to plot the results
// container needed to control the size of the canvas
const container = document.createElement('div')
container.style.width = width + 'px'
container.style.height = height + 'px'
document.body.append(container)
// the canvas for plotting
const canvas = document.createElement('canvas')
container.append(canvas)
// Chart-class from chartjs
const chart = new Chart(canvas, { // eslint-disable-line
type: 'scatter',
data: {
datasets: [{
label: label,
data: points,
showLine: true,
fill: false,
tension: 0,
borderColor: 'black'
}]
},
options: {
maintainAspectRatio: false,
responsive: true
}
})
}
function exampleEquation1 (x, y) {
return x
}
// example from https://en.wikipedia.org/wiki/Euler_method
function exampleEquation2 (x, y) {
return y
}
// example from https://www.geeksforgeeks.org/euler-method-solving-differential-equation/
function exampleEquation3 (x, y) {
return x + y + x * y
}
// plot the results if the script is executed in a browser with a window-object
if (typeof window !== 'undefined') {
const points1 = eulerFull(0, 4, 0.1, 0, exampleEquation1)
const points2 = eulerFull(0, 4, 0.1, 1, exampleEquation2)
const points3 = eulerFull(0, 0.1, 0.025, 1, exampleEquation3)
const script = document.createElement('script')
// using chartjs
script.src = 'https://www.chartjs.org/dist/2.9.4/Chart.min.js'
script.onload = function () {
plotLine('example 1: dy/dx = x', points1, 600, 400)
plotLine('example 2: dy/dx = y', points2, 600, 400)
plotLine('example 3: dy/dx = x + y + x * y', points3, 600, 400)
}
document.body.append(script)
}

View File

@ -0,0 +1,18 @@
import { eulerFull, eulerStep } from '../EulerMethod'
describe('eulerStep', () => {
it('should calculate the next y value correctly', () => {
expect(eulerStep(0, 0.1, 0, function (x, y) { return x })).toBe(0)
expect(eulerStep(2, 1, 1, function (x, y) { return x * x })).toBe(5)
})
})
describe('eulerFull', () => {
it('should return all the points found', () => {
expect(eulerFull(0, 3, 1, 0, function (x, y) { return x }))
.toEqual([{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 2, y: 1 }, { x: 3, y: 3 }])
expect(eulerFull(3, 4, 0.5, 1, function (x, y) { return x * x }))
.toEqual([{ x: 3, y: 1 }, { x: 3.5, y: 5.5 }, { x: 4, y: 11.625 }])
})
})

View File

@ -0,0 +1,11 @@
import { EulersTotient } from '../EulersTotient'
describe('EulersTotient', () => {
it('should return 6 as 1, 2, 4, 5, 7, and 8 are coprime to 9', () => {
expect(EulersTotient(9)).toBe(6)
})
it('should return 4 as 1, 3, 7, and 9 are coprime to 10', () => {
expect(EulersTotient(10)).toBe(4)
})
})

View File

@ -0,0 +1,20 @@
import { getRGBData } from '../Mandelbrot'
// plot the results if the script is executed in a browser with a window-object
if (typeof window !== 'undefined') {
const rgbData = getRGBData()
const width = rgbData.length
const height = rgbData[0].length
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
const ctx = canvas.getContext('2d')
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const rgb = rgbData[x][y]
ctx.fillStyle = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'
ctx.fillRect(x, y, 1, 1)
}
}
document.body.append(canvas)
}

View File

@ -0,0 +1,21 @@
import { getRGBData } from '../Mandelbrot'
describe('Mandelbrot', () => {
it('should produce black pixels inside the set', () => {
const blackAndWhite = getRGBData(800, 600, -0.6, 0, 3.2, 50, false)
expect(blackAndWhite[400][300]).toEqual([0, 0, 0]) // black
const colorCoded = getRGBData(800, 600, -0.6, 0, 3.2, 50, true)
expect(colorCoded[400][300]).toEqual([0, 0, 0]) // black
})
it('should produce white pixels outside of the set', () => {
const blackAndWhite = getRGBData(800, 600, -0.6, 0, 3.2, 50, false)
expect(blackAndWhite[0][0]).toEqual([255, 255, 255]) // black
})
it('should produce colored pixels distant to the set', () => {
const colorCoded = getRGBData(800, 600, -0.6, 0, 3.2, 50, true)
expect(colorCoded[0][0]).toEqual([255, 0, 0]) // red
})
})

View File

@ -0,0 +1,11 @@
import { PrimeFactors } from '../PrimeFactors'
describe('EulersTotient', () => {
it('should return the prime factors for 100', () => {
expect(PrimeFactors(100)).toEqual([2, 2, 5, 5])
})
it('should return the prime factors for 2560', () => {
expect(PrimeFactors(2560)).toEqual([2, 2, 2, 2, 2, 2, 2, 2, 2, 5])
})
})

View File

@ -1,19 +1,19 @@
# The Algorithms - JavaScript
[![contributions welcome](https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3&style=flat-square)](https://github.com/TheAlgorithms/Javascript/blob/master/CONTRIBUTING.md)&nbsp;
[![contributions welcome](https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3&style=flat-square)](CONTRIBUTING.md)
[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/TheAlgorithms/Javascript.svg?logo=lgtm&logoWidth=18&style=flat-square)](https://lgtm.com/projects/g/TheAlgorithms/Javascript/context:javascript)
![Node CI](https://github.com/TheAlgorithms/Javascript/workflows/Node%20CI/badge.svg)
![update_directory_md](https://github.com/TheAlgorithms/Javascript/workflows/update_directory_md/badge.svg)
[![Discord chat](https://img.shields.io/discord/808045925556682782.svg?logo=discord&colorB=7289DA&style=flat-square)](https://discord.gg/c7MnfGFGa6)
![](https://img.shields.io/github/repo-size/TheAlgorithms/Javascript.svg?label=Repo%20size&style=flat-square)&nbsp;
![](https://img.shields.io/github/repo-size/TheAlgorithms/Javascript.svg?label=Repo%20size&style=flat-square)
[![standard.js](https://img.shields.io/badge/code%20style-standardjs-%23f3df49)](https://standardjs.com/)
## All algorithms implemented in JavaScript (for educational purposes only)
### All algorithms implemented in JavaScript (for educational purposes only)
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/TheAlgorithms/Javascript)&nbsp;
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/TheAlgorithms/Javascript)
These are for demonstration purposes only. There are many implementations of sorts in the JavaScript standard library that are much better for performance reasons.
These are for demonstration purposes only. There are many implementations of sorts in the JavaScript standard library
that are much better for performance reasons.
## Contribution Guidelines

View File

@ -1,44 +1,25 @@
/**
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected
* to a given node in a multi-dimensional array with some matching attribute. It is used in the
* "bucket" fill tool of paint programs to fill connected, similarly-colored areas with a different
* color. (description adapted from https://en.wikipedia.org/wiki/Flood_fill) (see also:
* https://www.techiedelight.com/flood-fill-algorithm/).
* Flood fill.
*
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected to a given node in a
* multi-dimensional array with some matching attribute. It is used in the "bucket" fill tool of paint programs to fill
* connected, similarly-colored areas with a different color.
*
* (description adapted from https://en.wikipedia.org/wiki/Flood_fill)
* @see https://www.techiedelight.com/flood-fill-algorithm/
*/
const neighbors = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
const black = [0, 0, 0]
const green = [0, 255, 0]
const violet = [255, 0, 255]
const white = [255, 255, 255]
const orange = [255, 128, 0] // eslint-disable-line
/*
Doctests
> testBreadthFirst([1, 1], green, orange, [1, 1]);
orange
> testBreadthFirst([1, 1], green, orange, [0, 1]);
violet
> testBreadthFirst([1, 1], green, orange, [6, 4]);
white
> testDepthFirst([1, 1], green, orange, [1, 1]);
orange
> testDepthFirst([1, 1], green, orange, [0, 1]);
violet
> testDepthFirst([1, 1], green, orange, [6, 4]);
white
*/
/**
* Implements the flood fill algorithm through a breadth-first approach using a queue.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
* Implements the flood fill algorithm through a breadth-first approach using a queue.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
export function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
if (location[0] < 0 ||
location[0] >= rgbData.length ||
location[1] < 0 ||
@ -55,14 +36,14 @@ function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
}
/**
* Implements the flood fill algorithm through a depth-first approach using recursion.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
* Implements the flood fill algorithm through a depth-first approach using recursion.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
export function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
if (location[0] < 0 ||
location[0] >= rgbData.length ||
location[1] < 0 ||
@ -74,14 +55,14 @@ function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
}
/**
* Utility-function to implement the breadth-first loop
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param queue The locations that still need to be visited.
*/
* Utility-function to implement the breadth-first loop.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param queue The locations that still need to be visited.
*/
function breadthFirstFill (rgbData, location, targetColor, replacementColor, queue) {
const currentLocation = queue[0]
queue.shift()
@ -100,13 +81,13 @@ function breadthFirstFill (rgbData, location, targetColor, replacementColor, que
}
/**
* Utility-function to implement the depth-first loop
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
* Utility-function to implement the depth-first loop.
*
* @param rgbData The image to which the algorithm is applied.
* @param location The start location on the image.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
*/
function depthFirstFill (rgbData, location, targetColor, replacementColor) {
if (rgbData[location[0]][location[1]] === targetColor) {
rgbData[location[0]][location[1]] = replacementColor
@ -120,61 +101,3 @@ function depthFirstFill (rgbData, location, targetColor, replacementColor) {
}
}
}
/**
* Generates the rgbData-matrix for the tests
*
* @return example rgbData-matrix
*/
function generateTestRgbData () {
const layout = [
[violet, violet, green, green, black, green, green],
[violet, green, green, black, green, green, green],
[green, green, green, black, green, green, green],
[black, black, green, black, white, white, green],
[violet, violet, black, violet, violet, white, white],
[green, green, green, violet, violet, violet, violet],
[violet, violet, violet, violet, violet, violet, violet]
]
// transpose layout-matrix so the x-index comes before the y-index
const transposed = []
for (let x = 0; x < layout[0].length; x++) {
transposed[x] = []
for (let y = 0; y < layout.length; y++) {
transposed[x][y] = layout[y][x]
}
}
return transposed
}
/**
* Utility-function to test the function "breadthFirstSearch"
*
* @param fillLocation The start location on the image where the flood fill is applied.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param testLocation The location of the color to be checked.
* @return The color at testLocation
*/
function testBreadthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
const rgbData = generateTestRgbData()
breadthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
return rgbData[testLocation[0]][testLocation[1]]
}
/**
* Utility-function to test the function "depthFirstSearch"
*
* @param fillLocation The start location on the image where the flood fill is applied.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param testLocation The location of the color to be checked.
* @return The color at testLocation
*/
function testDepthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
const rgbData = generateTestRgbData()
depthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
return rgbData[testLocation[0]][testLocation[1]]
}

View File

@ -0,0 +1,80 @@
import { breadthFirstSearch, depthFirstSearch } from './FloodFill'
// some constants
const black = [0, 0, 0]
const green = [0, 255, 0]
const violet = [255, 0, 255]
const white = [255, 255, 255]
const orange = [255, 128, 0]
describe('FloodFill', () => {
it('should calculate the correct colors using breadth-first approach', () => {
expect(testBreadthFirst([1, 1], green, orange, [1, 1])).toEqual(orange)
expect(testBreadthFirst([1, 1], green, orange, [0, 1])).toEqual(violet)
expect(testBreadthFirst([1, 1], green, orange, [6, 4])).toEqual(white)
})
it('should calculate the correct colors using depth-first approach', () => {
expect(testDepthFirst([1, 1], green, orange, [1, 1])).toEqual(orange)
expect(testDepthFirst([1, 1], green, orange, [0, 1])).toEqual(violet)
expect(testDepthFirst([1, 1], green, orange, [6, 4])).toEqual(white)
})
})
/**
* Utility-function to test the function "breadthFirstSearch".
*
* @param fillLocation The start location on the image where the flood fill is applied.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param testLocation The location of the color to be checked.
* @return The color at testLocation.
*/
function testBreadthFirst (fillLocation, targetColor, replacementColor, testLocation) {
const rgbData = generateTestRgbData()
breadthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
return rgbData[testLocation[0]][testLocation[1]]
}
/**
* Utility-function to test the function "depthFirstSearch".
*
* @param fillLocation The start location on the image where the flood fill is applied.
* @param targetColor The old color to be replaced.
* @param replacementColor The new color to replace the old one.
* @param testLocation The location of the color to be checked.
* @return The color at testLocation.
*/
function testDepthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
const rgbData = generateTestRgbData()
depthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
return rgbData[testLocation[0]][testLocation[1]]
}
/**
* Generates the rgbData-matrix for the tests.
*
* @return example rgbData-matrix.
*/
function generateTestRgbData () {
const layout = [
[violet, violet, green, green, black, green, green],
[violet, green, green, black, green, green, green],
[green, green, green, black, green, green, green],
[black, black, green, black, white, white, green],
[violet, violet, black, violet, violet, white, white],
[green, green, green, violet, violet, violet, violet],
[violet, violet, violet, violet, violet, violet, violet]
]
// transpose layout-matrix so the x-index comes before the y-index
const transposed = []
for (let x = 0; x < layout[0].length; x++) {
transposed[x] = []
for (let y = 0; y < layout.length; y++) {
transposed[x][y] = layout[y][x]
}
}
return transposed
}

View File

@ -1,34 +1,20 @@
/**
* The Koch snowflake is a fractal curve and one of the earliest fractals to have been described.
* The Koch snowflake can be built up iteratively, in a sequence of stages. The first stage is an
* equilateral triangle, and each successive stage is formed by adding outward bends to each side of
* the previous stage, making smaller equilateral triangles. This can be achieved through the
* following steps for each line: 1. divide the line segment into three segments of equal length. 2.
* draw an equilateral triangle that has the middle segment from step 1 as its base and points
* outward. 3. remove the line segment that is the base of the triangle from step 2. (description
* adapted from https://en.wikipedia.org/wiki/Koch_snowflake ) (for a more detailed explanation and
* an implementation in the Processing language, see
* https://natureofcode.com/book/chapter-8-fractals/ #84-the-koch-curve-and-the-arraylist-technique
* ).
*
* The Koch snowflake can be built up iteratively, in a sequence of stages. The first stage is an equilateral triangle,
* and each successive stage is formed by adding outward bends to each side of the previous stage, making smaller
* equilateral triangles. This can be achieved through the following steps for each line:
* 1. divide the line segment into three segments of equal length.
* 2. draw an equilateral triangle that has the middle segment from step 1 as its base and points outward.
* 3. remove the line segment that is the base of the triangle from step 2.
*
* (description adapted from https://en.wikipedia.org/wiki/Koch_snowflake)
* (for a more detailed explanation and an implementation in the Processing language, see
* https://natureofcode.com/book/chapter-8-fractals/ #84-the-koch-curve-and-the-arraylist-technique).
*/
/*
Doctests
Test iterate-method
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[0];
{"x": 0, "y": 0}
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[1];
{"x": 1/3, "y": 0}
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[2];
{"x": 1/2, "y": Math.sin(Math.PI / 3) / 3}
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[3];
{"x": 2/3, "y": 0}
> iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[4];
{"x": 1, "y": 0}
*/
/** Class to handle the vector calculations. */
class Vector2 {
export class Vector2 {
constructor (x, y) {
this.x = x
this.y = y
@ -87,66 +73,15 @@ class Vector2 {
}
/**
* Method to render the Koch snowflake to a canvas.
* Go through the number of iterations determined by the argument "steps".
*
* @param canvasWidth The width of the canvas.
* @param steps The number of iterations.
* @returns The canvas of the rendered Koch snowflake.
*/
function getKochSnowflake (canvasWidth = 600, steps = 5) {
if (canvasWidth <= 0) {
throw new Error('canvasWidth should be greater than zero')
}
const offsetX = canvasWidth / 10.0
const offsetY = canvasWidth / 3.7
const vector1 = new Vector2(offsetX, offsetY)
const vector2 =
new Vector2(canvasWidth / 2, Math.sin(Math.PI / 3) * canvasWidth * 0.8 + offsetY)
const vector3 = new Vector2(canvasWidth - offsetX, offsetY)
const initialVectors = []
initialVectors.push(vector1)
initialVectors.push(vector2)
initialVectors.push(vector3)
initialVectors.push(vector1)
const vectors = iterate(initialVectors, steps)
return drawToCanvas(vectors, canvasWidth, canvasWidth)
}
/**
* Utility-method to render the Koch snowflake to a canvas.
*
* @param vectors The vectors defining the edges to be rendered.
* @param canvasWidth The width of the canvas.
* @param canvasHeight The height of the canvas.
* @returns The canvas of the rendered edges.
*/
function drawToCanvas (vectors, canvasWidth, canvasHeight) {
const canvas = document.createElement('canvas')
canvas.width = canvasWidth
canvas.height = canvasHeight
// Draw the edges
const ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.moveTo(vectors[0].x, vectors[0].y)
for (let i = 1; i < vectors.length; i++) {
ctx.lineTo(vectors[i].x, vectors[i].y)
}
ctx.stroke()
return canvas
}
/**
* Go through the number of iterations determined by the argument "steps". Be careful with high
* values (above 5) since the time to calculate increases exponentially.
* Be careful with high values (above 5) since the time to calculate increases exponentially.
*
* @param initialVectors The vectors composing the shape to which the algorithm is applied.
* @param steps The number of iterations.
* @returns The transformed vectors after the iteration-steps.
*/
function iterate (initialVectors, steps) {
export function iterate (initialVectors, steps) {
let vectors = initialVectors
for (let i = 0; i < steps; i++) {
vectors = iterationStep(vectors)
@ -156,9 +91,10 @@ function iterate (initialVectors, steps) {
}
/**
* Loops through each pair of adjacent vectors. Each line between two adjacent vectors is divided
* into 4 segments by adding 3 additional vectors in-between the original two vectors. The vector
* in the middle is constructed through a 60 degree rotation so it is bent outwards.
* Loops through each pair of adjacent vectors.
*
* Each line between two adjacent vectors is divided into 4 segments by adding 3 additional vectors in-between the
* original two vectors. The vector in the middle is constructed through a 60 degree rotation so it is bent outwards.
*
* @param vectors The vectors composing the shape to which the algorithm is applied.
* @returns The transformed vectors after the iteration-step.
@ -178,9 +114,3 @@ function iterationStep (vectors) {
newVectors.push(vectors[vectors.length - 1])
return newVectors
}
// plot the results if the script is executed in a browser with a window-object
if (typeof window !== 'undefined') {
const canvas = getKochSnowflake()
document.body.append(canvas)
}

View File

@ -0,0 +1,58 @@
import { Vector2, iterate } from './KochSnowflake'
/**
* Method to render the Koch snowflake to a canvas.
*
* @param canvasWidth The width of the canvas.
* @param steps The number of iterations.
* @returns The canvas of the rendered Koch snowflake.
*/
function getKochSnowflake (canvasWidth = 600, steps = 5) {
if (canvasWidth <= 0) {
throw new Error('canvasWidth should be greater than zero')
}
const offsetX = canvasWidth / 10.0
const offsetY = canvasWidth / 3.7
const vector1 = new Vector2(offsetX, offsetY)
const vector2 = new Vector2(canvasWidth / 2, Math.sin(Math.PI / 3) * canvasWidth * 0.8 + offsetY)
const vector3 = new Vector2(canvasWidth - offsetX, offsetY)
const initialVectors = []
initialVectors.push(vector1)
initialVectors.push(vector2)
initialVectors.push(vector3)
initialVectors.push(vector1)
const vectors = iterate(initialVectors, steps)
return drawToCanvas(vectors, canvasWidth, canvasWidth)
}
/**
* Utility-method to render the Koch snowflake to a canvas.
*
* @param vectors The vectors defining the edges to be rendered.
* @param canvasWidth The width of the canvas.
* @param canvasHeight The height of the canvas.
* @returns The canvas of the rendered edges.
*/
function drawToCanvas (vectors, canvasWidth, canvasHeight) {
const canvas = document.createElement('canvas')
canvas.width = canvasWidth
canvas.height = canvasHeight
// Draw the edges
const ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.moveTo(vectors[0].x, vectors[0].y)
for (let i = 1; i < vectors.length; i++) {
ctx.lineTo(vectors[i].x, vectors[i].y)
}
ctx.stroke()
return canvas
}
// plot the results if the script is executed in a browser with a window-object
if (typeof window !== 'undefined') {
const canvas = getKochSnowflake()
document.body.append(canvas)
}

View File

@ -0,0 +1,20 @@
import { iterate, Vector2 } from './KochSnowflake'
describe('KochSnowflake', () => {
it('should produce the correctly-transformed vectors', () => {
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[0])
.toEqual({ x: 0, y: 0 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[1])
.toEqual({ x: 1 / 3, y: 0 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[2])
.toEqual({ x: 1 / 2, y: Math.sin(Math.PI / 3) / 3 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[3])
.toEqual({ x: 2 / 3, y: 0 })
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[4])
.toEqual({ x: 1, y: 0 })
})
})

View File

@ -11,25 +11,6 @@
* Reference: https://www.geeksforgeeks.org/ternary-search/
*/
/*
* Doctests
*
* > ternarySearchRecursive([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)
* 2
* > ternarySearchIterative([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 8)
* 7
* > ternarySearchRecursive([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0)
* -1
* > ternarySearchIterative([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 12)
* -1
* > ternarySearchRecursive(["Ali", "Cathrynli", "Josuke", "Thomas"], "Cathrynli")
* 1
* > ternarySearchRecursive(["Ali", "Cathrynli", "Josuke", "Thomas"], "Josuke")
* 2
* > ternarySearchRecursive(["Ali", "Cathrynli", "Josuke", "Thomas"], "Angela")
* -1
*/
function ternarySearchRecursive (arr, key, low = 0, high = arr.length - 1) {
if (high >= low) {
// find the mid1 and mid2
@ -65,6 +46,7 @@ function ternarySearchRecursive (arr, key, low = 0, high = arr.length - 1) {
return -1
}
}
function ternarySearchIterative (arr, key, low = 0, high = arr.length - 1) {
while (high >= low) {
// find the mid1 and mid2

View File

@ -1,25 +1,14 @@
/**
* Bead Sort, also known as Gravity sort, this algorithm was
* inspired from natural phenomenons and was designed keeping in mind objects(or beads)
* falling under the influence of gravity.
* Bead Sort, also known as Gravity sort.
*
* This algorithm was inspired from natural phenomena and was designed keeping in mind objects (or beads) falling under
* the influence of gravity.
*
* NOTE: It only works for arrays of positive integers.
*
* Wikipedia: https://en.wikipedia.org/wiki/Bead_sort
*/
/**
* Doctests
*
* > beadSort([5, 4, 3, 2, 1])
* [1, 2, 3, 4, 5]
* > beadSort([7, 9, 4, 3, 5])
* [3, 4, 5, 7, 9]
* > beadSort([-1, 5, 8, 4, 3, 19])
* ! RangeError: Sequence must be a list of positive integers!
*/
function beadSort (sequence) {
export function beadSort (sequence) {
/* Let's ensure our sequence has only Positive Integers */
if (sequence.some((integer) => integer < 0)) {
throw RangeError('Sequence must be a list of Positive integers Only!')
@ -60,23 +49,8 @@ function beadSort (sequence) {
}
/* Finally, let's turn our Bead rows into their Respective Numbers */
const sortedSequence = grid.map((beadArray) => {
return grid.map((beadArray) => {
const beadsArray = beadArray.filter(bead => bead === '*')
return beadsArray.length
})
return sortedSequence
}
/**
* Implementation of Bead Sort
*/
const array = [5, 4, 3, 2, 1]
// Before Sort
console.log('\n- Before Sort | Implementation of Bead Sort -')
console.log(array)
// After Sort
console.log('- After Sort | Implementation of Bead Sort -')
console.log(beadSort(array))
console.log('\n')

View File

@ -1,56 +1,38 @@
/*
* A simple helper function that checks, if the array is
* sorted in ascending order.
/**
* Checks whether the given array is sorted in ascending order.
*/
// > [].isSorted()
// true
// > [1].isSorted()
// true
// > [1,2,3].isSorted()
// true
// > [3,2,1].isSorted()
// false
/* eslint no-extend-native: ["off", { "exceptions": ["Object"] }] */
Array.prototype.isSorted = function () {
const length = this.length
export function isSorted (array) {
const length = array.length
for (let i = 0; i < length - 1; i++) {
if (this[i] > this[i + 1]) {
if (array[i] > array[i + 1]) {
return false
}
}
return true
}
/*
* A simple helper function to shuffle the array randomly in place.
/**
* Shuffles the given array randomly in place.
*/
Array.prototype.shuffle = function () {
for (let i = this.length - 1; i; i--) {
function shuffle (array) {
for (let i = array.length - 1; i; i--) {
const m = Math.floor(Math.random() * i)
const n = this[i - 1]
this[i - 1] = this[m]
this[m] = n
const n = array[i - 1]
array[i - 1] = array[m]
array[m] = n
}
}
/*
* Implementation of the bogosort algorithm. This sorting algorithm randomly
* rearranges the array until it is sorted.
/**
* Implementation of the bogosort algorithm.
*
* This sorting algorithm randomly rearranges the array until it is sorted.
*
* For more information see: https://en.wikipedia.org/wiki/Bogosort
*/
function bogoSort (items) {
while (!items.isSorted()) {
items.shuffle()
export function bogoSort (items) {
while (!isSorted(items)) {
shuffle(items)
}
return items
}
// Implementation of bogoSort
const ar = [5, 6, 7, 8, 1, 2, 12, 14]
// Array before Sort
console.log(ar)
bogoSort(ar)
// Array after sort
console.log(ar)

View File

@ -12,28 +12,10 @@
* Animated Visual: https://www.toptal.com/developers/sorting-algorithms/bubble-sort
*/
/*
* Doctests
*
* > bubbleSort([5, 4, 1, 2, 3])
* [1, 2, 3, 4, 5]
* > bubbleSort([])
* []
* > bubbleSort([1, 2, 3])
* [1, 2, 3]
*
* > alternativeBubbleSort([5, 4, 1, 2, 3])
* [1, 2, 3, 4, 5]
* > alternativeBubbleSort([])
* []
* > alternativeBubbleSort([1, 2, 3])
* [1, 2, 3]
*/
/*
* Using 2 for loops
*/
function bubbleSort (items) {
/**
* Using 2 for loops.
*/
export function bubbleSort (items) {
const length = items.length
for (let i = (length - 1); i > 0; i--) {
@ -50,22 +32,10 @@ function bubbleSort (items) {
return items
}
/*
* Implementation of 2 for loops method
*/
const array1 = [5, 6, 7, 8, 1, 2, 12, 14]
// Before Sort
console.log('\n- Before Sort | Implementation using 2 for loops -')
console.log(array1)
// After Sort
console.log('- After Sort | Implementation using 2 for loops -')
console.log(bubbleSort(array1))
console.log('\n')
/*
* Using a while loop and a for loop
*/
function alternativeBubbleSort (arr) {
/**
* Using a while loop and a for loop.
*/
export function alternativeBubbleSort (arr) {
let swapped = true
while (swapped) {
@ -80,15 +50,3 @@ function alternativeBubbleSort (arr) {
return arr
}
/*
* Implementation of a while loop and a for loop method
*/
const array2 = [5, 6, 7, 8, 1, 2, 12, 14]
// Before Sort
console.log('\n- Before Sort | Implementation using a while loop and a for loop -')
console.log(array2)
// After Sort
console.log('- After Sort | Implementation using a while loop and a for loop -')
console.log(alternativeBubbleSort(array2))
console.log('\n')

View File

@ -1,25 +1,23 @@
/*
[Wikipedia says](https://en.wikipedia.org/wiki/Bucket_sort#:~:text=Bucket%20sort%2C%20or%20bin%20sort,applying%20the%20bucket%20sorting%20algorithm.&text=Sort%20each%20non%2Dempty%20bucket.): Bucket sort, or bin sort, is a sorting algorithm that works by distributing the
elements of an array into a number of buckets. Each bucket is then sorted individually, either using
a different sorting algorithm, or by recursively applying the bucket sorting algorithm. It is a
distribution sort, and is a cousin of radix sort in the most to least significant digit flavour.
Bucket sort is a generalization of pigeonhole sort. Bucket sort can be implemented with comparisons
and therefore can also be considered a comparison sort algorithm. The computational complexity estimates
involve the number of buckets.
Time Complexity of Solution:
Best Case O(n); Average Case O(n); Worst Case O(n)
*/
/**
* bucketSort returns an array of numbers sorted in increasing order.
* BucketSort implementation.
*
* Wikipedia says: Bucket sort, or bin sort, is a sorting algorithm that works by distributing the elements of an array
* into a number of buckets. Each bucket is then sorted individually, either using a different sorting algorithm, or by
* recursively applying the bucket sorting algorithm. It is a distribution sort, and is a cousin of radix sort in the
* most to least significant digit flavour. Bucket sort is a generalization of pigeonhole sort. Bucket sort can be
* implemented with comparisons and therefore can also be considered a comparison sort algorithm. The computational
* complexity estimates involve the number of buckets.
*
* @see https://en.wikipedia.org/wiki/Bucket_sort#:~:text=Bucket%20sort%2C%20or%20bin%20sort,applying%20the%20bucket%20sorting%20algorithm.&text=Sort%20each%20non%2Dempty%20bucket.
*
* Time Complexity of Solution:
* Best Case O(n); Average Case O(n); Worst Case O(n)
*
* @param {number[]} list The array of numbers to be sorted.
* @param {number} size The size of the buckets used. If not provided, size will be 5.
* @return {number[]} An array of numbers sorted in increasing order.
*/
function bucketSort (list, size) {
export function bucketSort (list, size) {
if (undefined === size) {
size = 5
}
@ -60,5 +58,3 @@ function bucketSort (list, size) {
}
return sorted
}
export { bucketSort }

View File

@ -1,26 +1,14 @@
/*
/**
* Cocktail Shaker Sort is an algorithm that is a Bidirectional Bubble Sort.
*
* The algorithm extends bubble sort by operating in two directions.
* While it improves on bubble sort by more quickly moving items to the beginning of the list,
* it provides only marginal performance improvements.
* While it improves on bubble sort by more quickly moving items to the beginning of the list, it provides only marginal
* performance improvements.
*
* Wikipedia (Cocktail Shaker Sort): https://en.wikipedia.org/wiki/Cocktail_shaker_sort
* Wikipedia (Bubble Sort): https://en.wikipedia.org/wiki/Bubble_sort
*
*/
/**
* Doctests
*
* > cocktailShakerSort([5, 4, 1, 2, 3])
* [1, 2, 3, 4, 5]
* > cocktailShakerSort([])
* []
* > cocktailShakerSort([1, 2, 3])
* [1, 2, 3]
*/
function cocktailShakerSort (items) {
export function cocktailShakerSort (items) {
for (let i = items.length - 1; i > 0; i--) {
let j
@ -41,15 +29,3 @@ function cocktailShakerSort (items) {
return items
}
/**
* Implementation of Cocktail Shaker Sort
*/
const array = [5, 6, 7, 8, 1, 2, 12, 14]
// Before Sort
console.log('\n- Before Sort | Implementation of Cocktail Shaker Sort -')
console.log(array)
// After Sort
console.log('- After Sort | Implementation of Cocktail Shaker Sort -')
console.log(cocktailShakerSort(array))
console.log('\n')

View File

@ -1,40 +1,21 @@
/**
* Merge Sort is an algorithm where the main list is divided down into two half
* sized lists, which then have merge sort called on these two smaller lists
* recursively until there is only a sorted list of one.
/*
* MergeSort implementation.
*
* Merge Sort is an algorithm where the main list is divided down into two half sized lists, which then have merge sort
* called on these two smaller lists recursively until there is only a sorted list of one.
*
* On the way up the recursive calls, the lists will be merged together inserting
* the smaller value first, creating a larger sorted list.
*/
/**
* Sort and merge two given arrays
* @param {Array} list1 - sublist to break down
* @param {Array} list2 - sublist to break down
* @return {Array} merged list
* Sort and merge two given arrays.
*
* @param {Array} list1 Sublist to break down.
* @param {Array} list2 Sublist to break down.
* @return {Array} The merged list.
*/
/*
* Doctests
* > merge([5, 4],[ 1, 2, 3])
* [1, 2, 3, 5, 4]
* > merge([],[1, 2])
* [1, 2]
* > merge([1, 2, 3], [1])
* [1, 1, 2, 3]
* > merge([], [])
* []
*
* > mergeSort([5, 4])
* [4, 5]
* > mergeSort([8, 4, 10, 15, 9])
* [4, 8, 9, 10, 15]
* > mergeSort([1, 2, 3])
* [1, 2, 3]
* > mergeSort([ ])
* [ ]
*/
function merge (list1, list2) {
export function merge (list1, list2) {
const results = []
let i = 0
let j = 0
@ -51,11 +32,12 @@ function merge (list1, list2) {
}
/**
* Break down the lists into smaller pieces to be merged
* @param {Array} list - list to be sorted
* @return {Array} sorted list
* Break down the lists into smaller pieces to be merged.
*
* @param {Array} list List to be sorted.
* @return {Array} The sorted list.
*/
function mergeSort (list) {
export function mergeSort (list) {
if (list.length < 2) return list
const listHalf = Math.floor(list.length / 2)
@ -64,9 +46,3 @@ function mergeSort (list) {
return merge(mergeSort(subList1), mergeSort(subList2))
}
// Merge Sort Example
const unsortedArray = [10, 5, 3, 8, 2, 6, 4, 7, 9, 1]
const sortedArray = mergeSort(unsortedArray)
console.log('Before:', unsortedArray, 'After:', sortedArray)

View File

@ -1,22 +1,9 @@
/*
/**
* Quick sort is a comparison sorting algorithm that uses a divide and conquer strategy.
*
* For more information see here: https://en.wikipedia.org/wiki/Quicksort
*/
/*
* Doctests
*
* > quickSort([5, 4, 3, 10, 2, 1])
* [1, 2, 3, 4, 5, 10]
* > quickSort([])
* []
* > quickSort([5, 4])
* [4, 5]
* > quickSort([1, 2, 3])
* [1, 2, 3]
*/
function quickSort (items) {
export function quickSort (items) {
const length = items.length
if (length <= 1) {
@ -40,12 +27,3 @@ function quickSort (items) {
return sorted
}
// Implementation of quick sort
let ar = [0, 5, 3, 2, 2]
// Array before Sort
console.log(ar)
ar = quickSort(ar)
// Array after sort
console.log(ar)

View File

@ -0,0 +1,12 @@
import { beadSort } from '../BeadSort'
describe('BeadSort', () => {
it('should sort arrays correctly', () => {
expect(beadSort([5, 4, 3, 2, 1])).toEqual([1, 2, 3, 4, 5])
expect(beadSort([7, 9, 4, 3, 5])).toEqual([3, 4, 5, 7, 9])
})
it('should throw a RangeError when the array contains negative integers', () => {
expect(() => beadSort([-1, 5, 8, 4, 3, 19])).toThrow(RangeError)
})
})

View File

@ -0,0 +1,25 @@
import { bogoSort, isSorted } from '../BogoSort'
describe('isSorted', () => {
it('should return true for empty arrays', () => {
expect(isSorted([])).toBe(true)
})
it('should return true for single-element arrays', () => {
expect(isSorted([1])).toBe(true)
})
it('should return true for arrays that are properly sorted', () => {
expect(isSorted([1, 2, 3])).toBe(true)
})
it('should return false for arrays that are not properly sorted', () => {
expect(isSorted([3, 2, 1])).toBe(false)
})
})
describe('bogoSort', () => {
it('should (eventually) sort the array', () => {
expect(bogoSort([5, 6, 7, 8, 1, 2, 12, 14])).toEqual([1, 2, 5, 6, 7, 8, 12, 14])
})
})

View File

@ -0,0 +1,19 @@
import { alternativeBubbleSort, bubbleSort } from '../BubbleSort'
describe('bubbleSort', () => {
it('should sort arrays correctly', () => {
expect(bubbleSort([5, 4, 1, 2, 3])).toEqual([1, 2, 3, 4, 5])
expect(bubbleSort([])).toEqual([])
expect(bubbleSort([1, 2, 3])).toEqual([1, 2, 3])
expect(bubbleSort([5, 6, 7, 8, 1, 2, 12, 14])).toEqual([1, 2, 5, 6, 7, 8, 12, 14])
})
})
describe('alternativeBubbleSort', () => {
it('should sort arrays correctly', () => {
expect(alternativeBubbleSort([5, 4, 1, 2, 3])).toEqual([1, 2, 3, 4, 5])
expect(alternativeBubbleSort([])).toEqual([])
expect(alternativeBubbleSort([1, 2, 3])).toEqual([1, 2, 3])
expect(alternativeBubbleSort([5, 6, 7, 8, 1, 2, 12, 14])).toEqual([1, 2, 5, 6, 7, 8, 12, 14])
})
})

View File

@ -0,0 +1,13 @@
import { cocktailShakerSort } from '../CocktailShakerSort'
describe('CocktailShakerSort', () => {
it('should sort arrays correctly', () => {
expect(cocktailShakerSort([5, 4, 1, 2, 3])).toEqual([1, 2, 3, 4, 5])
expect(cocktailShakerSort([1, 2, 3])).toEqual([1, 2, 3])
expect(cocktailShakerSort([5, 6, 7, 8, 1, 2, 12, 14])).toEqual([1, 2, 5, 6, 7, 8, 12, 14])
})
it('should work for empty arrays, too', () => {
expect(cocktailShakerSort([])).toEqual([])
})
})

View File

@ -0,0 +1,23 @@
import { merge, mergeSort } from '../MergeSort'
describe('merge', () => {
it('should merge arrays correctly', () => {
expect(merge([5, 4], [1, 2, 3])).toEqual([1, 2, 3, 5, 4])
expect(merge([], [1, 2])).toEqual([1, 2])
expect(merge([1, 2, 3], [1])).toEqual([1, 1, 2, 3])
expect(merge([], [])).toEqual([])
})
})
describe('MergeSort', () => {
it('should work for empty arrays', () => {
expect(mergeSort([])).toEqual([])
})
it('should sort arrays correctly', () => {
expect(mergeSort([5, 4])).toEqual([4, 5])
expect(mergeSort([8, 4, 10, 15, 9])).toEqual([4, 8, 9, 10, 15])
expect(mergeSort([1, 2, 3])).toEqual([1, 2, 3])
expect(mergeSort([10, 5, 3, 8, 2, 6, 4, 7, 9, 1])).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
})
})

View File

@ -0,0 +1,14 @@
import { quickSort } from '../QuickSort'
describe('QuickSort', () => {
it('should work for empty arrays', () => {
expect(quickSort([])).toEqual([])
})
it('should sort arrays correctly', () => {
expect(quickSort([5, 4, 3, 10, 2, 1])).toEqual([1, 2, 3, 4, 5, 10])
expect(quickSort([5, 4])).toEqual([4, 5])
expect(quickSort([1, 2, 3])).toEqual([1, 2, 3])
expect(quickSort([0, 5, 3, 2, 2])).toEqual([0, 2, 2, 3, 5])
})
})

View File

@ -1,26 +1,5 @@
/**
* A short example showing how to reverse a string
* @flow
*/
/**
* Create a new string and append
* @complexity O(n)
*
* Doctests
*
* > ReverseStringIterative('some')
* 'emos'
* > ReverseStringIterative('string')
* 'gnirts'
* > ReverseStringIterative('The Algorithms Javascript')
* 'tpircsavaJ smhtiroglA ehT'
* > ReverseStringIterative([])
* ! TypeError
* > ReverseStringIterative({})
* ! TypeError
* > ReverseStringIterative(null)
* ! TypeError
* A short example showing how to reverse a string.
*/
function ReverseStringIterative (string) {
if (typeof string !== 'string') {
@ -40,34 +19,17 @@ function ReverseStringIterative (string) {
* JS disallows string mutation so we're actually a bit slower.
*
* @complexity O(n)
*
* 'some' -> 'eoms' -> 'emos'
*
* Doctests
*
* > ReverseStringIterativeInplace('some')
* 'emos'
* > ReverseStringIterativeInplace('string')
* 'gnirts'
* > ReverseStringIterativeInplace('The Algorithms Javascript')
* 'tpircsavaJ smhtiroglA ehT'
* > ReverseStringIterativeInplace([])
* ! TypeError
* > ReverseStringIterativeInplace({})
* ! TypeError
* > ReverseStringIterativeInplace(null)
* ! TypeError
*/
function ReverseStringIterativeInplace (string) {
if (typeof string !== 'string') {
throw new TypeError('The given value is not a string')
}
const _string = string.split('')
for (let i = 0; i < Math.floor(_string.length / 2); i++) {
const first = _string[i]
const second = _string[_string.length - 1 - i]
_string[i] = second
_string[i] = _string[_string.length - 1 - i]
_string[_string.length - 1 - i] = first
}

View File

@ -1,19 +1,3 @@
/*
* Doctests
*
* > reverseWords('I Love JS')
* 'JS Love I'
* > reverseWords('Hello World')
* 'World Hello'
* > reverseWords('The Algorithms Javascript')
* 'Javascript Algorithms The'
* > reverseWords([])
* ! TypeError
* > reverseWords({})
* ! TypeError
* > reverseWords(null)
* ! TypeError
*/
const reverseWords = (str) => {
if (typeof str !== 'string') {
throw new TypeError('The given value is not a string')

View File

@ -1,22 +1,6 @@
/*
Function that takes a string input and return either true or false
If it is a valid email address
*/
/*
* Doctests
*
* > validateEmail('mahfoudh.arous@gmail.com')
* true
* > validateEmail('mahfoudh.arous@helsinki.edu')
* true
* > validateEmail('mahfoudh.arous.com')
* false
* > validateEmail('')
* ! TypeError
* > validateEmail(null)
* ! TypeError
*/
/**
* Returns whether the given string is a valid email address or not.
*/
const validateEmail = (str) => {
if (str === '' || str === null) {
throw new TypeError('Email Address String Null or Empty.')

View File

@ -1,63 +1,65 @@
import {
ReverseStringIterative,
ReverseStringIterativeInplace
} from '../ReverseString'
import { ReverseStringIterative, ReverseStringIterativeInplace } from '../ReverseString'
describe('ReverseStringIterative', () => {
it('expects to reverse a simple string', () => {
const SUT = ReverseStringIterative('reverse')
expect(SUT).toEqual('esrever')
expect(ReverseStringIterative('reverse')).toEqual('esrever')
expect(ReverseStringIterative('some')).toEqual('emos')
expect(ReverseStringIterative('string')).toEqual('gnirts')
expect(ReverseStringIterative('The Algorithms Javascript')).toEqual('tpircsavaJ smhtiroglA ehT')
})
it('expects to reverse a string with spaces in between', () => {
const SUT = ReverseStringIterative('reverse me')
expect(SUT).toEqual('em esrever')
expect(ReverseStringIterative('reverse me')).toEqual('em esrever')
})
it('expects to reverse a simple string without capitalizing the first letter', () => {
const SUT = ReverseStringIterative('Javascript')
expect(SUT).toEqual('tpircsavaJ')
expect(ReverseStringIterative('Javascript')).toEqual('tpircsavaJ')
})
it.each`
input
${123456}
${[1, 2, 3, 4, 5, 6]}
${{ test: 'test' }}
${null}
`(
'expects to throw a type error given a value that is $input',
({ input }) => {
expect(() => {
ReverseStringIterative(input)
}).toThrow('The given value is not a string')
expect(() => ReverseStringIterative(input)).toThrow('The given value is not a string')
}
)
it('expects to return a empty string with an empty string is given', () => {
const SUT = ReverseStringIterative('')
expect(SUT).toEqual('')
expect(ReverseStringIterative('')).toEqual('')
})
})
describe('ReverseStringIterativeInplace', () => {
it('expects to reverse a simple string', () => {
const SUT = ReverseStringIterativeInplace('reverse')
expect(SUT).toEqual('esrever')
expect(ReverseStringIterativeInplace('reverse')).toEqual('esrever')
expect(ReverseStringIterativeInplace('some')).toEqual('emos')
expect(ReverseStringIterativeInplace('string')).toEqual('gnirts')
expect(ReverseStringIterativeInplace('The Algorithms Javascript')).toEqual('tpircsavaJ smhtiroglA ehT')
})
it('expects to reverse a simple string without capitalizing the first letter', () => {
const SUT = ReverseStringIterativeInplace('Javascript')
expect(SUT).toEqual('tpircsavaJ')
expect(ReverseStringIterativeInplace('Javascript')).toEqual('tpircsavaJ')
})
it('expects to return an empty string given an empty string', () => {
const SUT = ReverseStringIterativeInplace('Javascript')
expect(SUT).toEqual('tpircsavaJ')
expect(ReverseStringIterativeInplace('Javascript')).toEqual('tpircsavaJ')
})
it.each`
input
${123456}
${[1, 2, 3, 4, 5, 6]}
${{ test: 'test' }}
${null}
`(
'expects to throw a type error given a value that is $input',
({ input }) => {
expect(() => {
ReverseStringIterativeInplace(input)
}).toThrow('The given value is not a string')
expect(() => ReverseStringIterativeInplace(input)).toThrow('The given value is not a string')
}
)
})

View File

@ -2,14 +2,17 @@ import { reverseWords } from '../ReverseWords'
describe('reverseWords', () => {
it('expects to reverse words to return a joined word', () => {
const SUT = reverseWords('I Love JS')
expect(SUT).toBe('JS Love I')
expect(reverseWords('I Love JS')).toBe('JS Love I')
expect(reverseWords('Hello World')).toBe('World Hello')
expect(reverseWords('The Algorithms Javascript')).toBe('Javascript Algorithms The')
})
it.each`
input
${123456}
${[1, 2, 3, 4, 5, 6]}
${{ test: 'test' }}
${null}
`(
'expects to throw a type error given a value that is $input',
({ input }) => {

View File

@ -19,5 +19,6 @@ describe('Validation of an Email Address', () => {
it('expects to throw a type error', () => {
expect(() => { validateEmail('') }).toThrow('Email Address String Null or Empty.')
expect(() => { validateEmail(null) }).toThrow('Email Address String Null or Empty.')
})
})

12483
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,8 @@
"description": "A repository for All algorithms implemented in Javascript (for educational purposes only)",
"main": "",
"scripts": {
"doctest": "doctest **/*.js",
"test": "jest --no-cache",
"style": "standard"
"style": "standard --fix"
},
"author": "TheAlgorithms",
"license": "GPL-3.0",
@ -26,9 +25,8 @@
},
"devDependencies": {
"babel-jest": "^26.3.0",
"doctest": "^0.17.1",
"globby": "^12.0.2",
"jest": "^26.4.2",
"standard": "^14.3.4"
}
}
}