@onesy/algorithms
Version:
510 lines (434 loc) • 19.3 kB
JavaScript
/** @license Algorithms v1.0.0
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Algorithms = {}));
})(this, (function (exports) { 'use strict';
const cache$3 = {};
function factorialRecursive(value) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
cache: true
};
// Cache
if (options?.cache && cache$3[value] !== undefined) return cache$3[value];
if (value < 3) {
if (options?.cache && cache$3[value] === undefined) cache$3[value] = value;
return value;
}
const result = value * factorialRecursive(value - 1, options);
if (options?.cache && cache$3[value] === undefined) cache$3[value] = result;
return result;
}
const cache$2 = {};
function factorial(value) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
cache: true
};
// Cache
if (options?.cache && cache$2[value] !== undefined) return cache$2[value];
if (value < 3) return value;
const values = [0, 1, 2];
for (let i = 3; i <= value; i++) values[i] = i * values[i - 1];
const result = values[values.length - 1];
if (options?.cache && cache$2[value] === undefined) cache$2[value] = result;
return result;
}
const cache$1 = {};
function fibonacciRecursive(value) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
cache: true
};
// Cache
if (options?.cache && cache$1[value] !== undefined) return cache$1[value];
if (value < 2) {
if (options?.cache && cache$1[value] === undefined) cache$1[value] = value;
return value;
}
const result = fibonacciRecursive(value - 1, options) + fibonacciRecursive(value - 2, options);
if (options?.cache && cache$1[value] === undefined) cache$1[value] = result;
return result;
}
const cache = {};
function fibonacci(value) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
cache: true
};
// Cache
if (options?.cache && cache[value] !== undefined) return cache[value];
if (value < 2) return value;
const values = [0, 1];
for (let i = 2; i <= value; i++) values[i] = values[i - 1] + values[i - 2];
const result = values[values.length - 1];
if (options?.cache && cache[value] === undefined) cache[value] = result;
return result;
}
var global$1 = (typeof global !== "undefined" ? global :
typeof self !== "undefined" ? self :
typeof window !== "undefined" ? window : {});
const optionsDefault = {};
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
const isNodejs = !!(typeof global$1 !== 'undefined' && typeof module !== 'undefined' && module.exports); // Multiple is methods instead of one,
// so it's lighter for tree shaking usability reasons
function is(type, value) {
var _value$constructor;
let options_ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
const options = { ...optionsDefault,
...options_
};
const {
variant
} = options;
const prototype = value && typeof value === 'object' && Object.getPrototypeOf(value);
switch (type) {
case 'string':
return typeof value === 'string';
case 'number':
return typeof value === 'number' && !Number.isNaN(value);
case 'boolean':
return typeof value === 'boolean';
case 'array':
return Array.isArray(value);
case 'object':
const isObject = typeof value === 'object' && !!value && value.constructor === Object;
return isObject;
// Map, null, WeakMap, Date, etc.
case 'object-like':
return typeof value === 'object' && (value === null || value.constructor !== Object);
case 'class':
return (typeof value === 'object' || typeof value === 'function') && (/class/gi.test(String(value)) || /class/gi.test(String(value === null || value === void 0 ? void 0 : value.constructor)));
case 'function':
return !!(value && value instanceof Function);
case 'async':
// If it's browser avoid calling the method
// to see if it's async func or not,
// where as in nodejs we have no other choice
// that i know of when using transpilation
// And also it might not be always correct, as
// a method that returns a promise is also async
// but we can't know that until the method is called and
// we inspect the method's return value
return !!(is('function', value) && (isBrowser ? value.constructor.name === 'AsyncFunction' : value() instanceof Promise));
case 'map':
return !!(prototype === Map.prototype);
case 'weakmap':
return !!(prototype === WeakMap.prototype);
case 'set':
return !!(prototype === Set.prototype);
case 'weakset':
return !!(prototype === WeakSet.prototype);
case 'promise':
return !!(prototype === Promise.prototype);
case 'int8array':
return !!(prototype === Int8Array.prototype);
case 'uint8array':
return !!(prototype === Uint8Array.prototype);
case 'uint8clampedarray':
return !!(prototype === Uint8ClampedArray.prototype);
case 'int16array':
return !!(prototype === Int16Array.prototype);
case 'uint16array':
return !!(prototype === Uint16Array.prototype);
case 'int32array':
return !!(prototype === Int32Array.prototype);
case 'uint32array':
return !!(prototype === Uint32Array.prototype);
case 'float32array':
return !!(prototype === Float32Array.prototype);
case 'float64array':
return !!(prototype === Float64Array.prototype);
case 'bigint64array':
return !!(prototype === BigInt64Array.prototype);
case 'biguint64array':
return !!(prototype === BigUint64Array.prototype);
case 'typedarray':
return is('int8array', value) || is('uint8array', value) || is('uint8clampedarray', value) || is('int16array', value) || is('uint16array', value) || is('int32array', value) || is('uint32array', value) || is('float32array', value) || is('float64array', value) || is('bigint64array', value) || is('biguint64array', value);
case 'dataview':
return !!(prototype === DataView.prototype);
case 'arraybuffer':
return !!(prototype === ArrayBuffer.prototype);
case 'sharedarraybuffer':
return typeof SharedArrayBuffer !== 'undefined' && !!(prototype === SharedArrayBuffer.prototype);
case 'symbol':
return !!(typeof value === 'symbol');
case 'error':
return !!(value && value instanceof Error);
case 'date':
return !!(value && value instanceof Date);
case 'regexp':
return !!(value && value instanceof RegExp);
case 'arguments':
return !!(value && value.toString() === '[object Arguments]');
case 'null':
return value === null;
case 'undefined':
return value === undefined;
case 'blob':
return isBrowser && value instanceof Blob;
case 'buffer':
return !!(isNodejs && typeof (value === null || value === void 0 ? void 0 : (_value$constructor = value.constructor) === null || _value$constructor === void 0 ? void 0 : _value$constructor.isBuffer) === 'function' && value.constructor.isBuffer(value));
case 'element':
if (value) {
switch (variant) {
case undefined:
case 'html':
case 'element':
return isBrowser && (typeof HTMLElement === 'object' ? value instanceof HTMLElement : value && typeof value === 'object' && value !== null && value.nodeType === 1 && typeof value.nodeName === 'string');
case 'node':
return isBrowser && (typeof Node === 'object' ? value instanceof Node : value && typeof value === 'object' && value !== null && typeof value.nodeType === 'number' && typeof value.nodeName === 'string');
case 'react':
return value.elementType || value.hasOwnProperty('$$typeof');
default:
return false;
}
}
return false;
case 'simple':
return is('string', value, options) || is('number', value, options) || is('boolean', value, options) || is('undefined', value, options) || is('null', value, options);
case 'not-array-object':
return !is('array', value, options) && !is('object', value, options);
default:
return false;
}
}
function binarySearch(array, value) {
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (options?.sort) array.sort(is('function', options?.sortMethod) ? options.sortMethod : (a, b) => a - b);
let start = 0;
let end = array.length - 1;
while (start <= end) {
const middle = start + Math.floor((end - start) / 2);
if (array[middle] === value) return middle;
if (value < array[middle]) end = middle - 1;else start = middle + 1;
}
return -1;
}
function binarySearchRecursive(array, value, startValue, endValue) {
let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
let start = startValue;
let end = endValue;
if (startValue === undefined && endValue === undefined) {
start = 0;
end = array.length - 1;
if (options?.sort) array.sort(is('function', options?.sortMethod) ? options.sortMethod : (a, b) => a - b);
}
if (end <= start && array[start] !== value) return -1;
const middle = start + Math.floor((end - start) / 2);
if (array[middle] === value) return middle;
return value < array[middle] ? binarySearchRecursive(array, value, start, middle - 1, options) : binarySearchRecursive(array, value, middle + 1, end, options);
}
// While loop runs, and in each loop, the entire array is looped and for every i,
// where i + 1 is smaller, swap between i and i + 1, is made.
// When no swaps are made in the entire array loop, they are all sorted.
// Complexity: O(n ^ 2)
function bubbleSort(value) {
let ascending = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
const length = value.length;
let swapped = true;
while (swapped) {
swapped = false;
for (let i = 0; i < length; i++) {
if (ascending ? value[i] > value[i + 1] : value[i] < value[i + 1]) {
const item = value[i];
value[i] = value[i + 1];
value[i + 1] = item;
swapped = true;
}
}
}
return value;
}
// For every i, an entire i + 1 slice of array is looped to find the min value(smaller than i) within it,
// and if that value is not the same as i, i and min are swapped.
// Complexity: O(n ^ 2)
function selectionSort(value) {
let ascending = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
const length = value.length;
for (let i = 0; i < length; i++) {
let minOrMax = i;
for (let j = i + 1; j < length; j++) {
if (ascending ? value[j] < value[minOrMax] : value[j] > value[minOrMax]) minOrMax = j;
}
if (minOrMax !== i) {
const item = value[i];
value[i] = value[minOrMax];
value[minOrMax] = item;
}
}
return value;
}
// Array is all the time split between sorted and unsorted part of the array.In every loop first element of unsorted part is compared to sorted part of the array from end to start(of sorted part), and every item from back to start in sorted part of the array that’s larger than first item in unsorted part of the array is swapped with item + 1 next to it.
// At the end, the last item from back to start in the sorted part that wasn’t bigger than the first item in sorted, is replaced with that first item in sorted part.
// Complexity: O(n ^ 2)
function insertionSort(value) {
let ascending = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
const length = value.length;
for (let i = 1; i < length; i++) {
const item = value[i];
let j = i - 1;
while (j >= 0 && (ascending ? value[j] > item : value[j] < item)) {
value[j + 1] = value[j];
j--;
}
value[j + 1] = item;
}
return value;
}
// Divides the array down the middle, recursively until last recursted method instance has array of length 1. And then starts merging back all those recursed instances from length of element 1, to the first 2 halves of the array, and merge them, checking left and right half and items in them, and pushing to resulting array items in order from lowest to highest, until recursion is back to the initial array.
// Complexity: O(n ^ 2)
function mergeSortResolve(array1, array2) {
let ascending = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
const result = [];
let i = 0;
let j = 0;
while (i < array1.length && j < array2.length) {
if (ascending ? array1[i] < array2[j] : array1[i] > array2[j]) {
result.push(array1[i]);
i++;
} else {
result.push(array2[j]);
j++;
}
}
while (i < array1.length) {
result.push(array1[i]);
i++;
}
while (j < array2.length) {
result.push(array2[j]);
j++;
}
return result;
}
function mergeSort(array) {
let ascending = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (array.length <= 1) return array;
const length = array.length;
const mid = Math.floor(length / 2);
const left = mergeSort(array.slice(0, mid), ascending);
const right = mergeSort(array.slice(mid), ascending);
return mergeSortResolve(left, right, ascending);
}
// Find a pivot item in the array, and go through all values from left to right, and all values less than the pivot value add them to left array, and all values bigger than pivot value, add them to the right array.Recursively now use the quick sort method on left and right arrays, and merge them back together with pivot value being in the middle.
// Complexity: O(n * log n)
function quickSort(array) {
let ascending = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
const length = array.length;
if (length <= 1) return array;
// Use the last value as the pivot value
const pivot = array[length - 1];
let i = 0;
const left = [];
const right = [];
// Do not include the pivot value
while (i < length - 1) {
if (ascending ? array[i] < pivot : array[i] > pivot) left.push(array[i]);else right.push(array[i]);
i++;
}
return [...quickSort(left, ascending), pivot, ...quickSort(right, ascending)];
}
// Including the sign (+,-)
function maxDigits(array) {
return Math.max(...array.map(item => String(item).length));
}
function getDigit(value, indexValue) {
let response = String(value);
const index = response.length - 1 - indexValue;
response = index >= 0 ? response[index] : undefined;
return ['+', '-'].includes(response) ? -1 : !response ? 0 : +response;
}
// Makes 10 buckets, and loops for the amount of times of number of digits in the largest number in the array.And in each iteration for every digit, loops through the entire array, and sorts from right digit to the left(smallest base number to largest), all the values in buckets, and flattens the array.At the end they will all be sorted in the last flattened array.
// Complexity: O(n)
function radixSort(value) {
let ascending = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
let array = value;
const digits = maxDigits(array);
// Digits
for (let i = 0; i < digits; i++) {
// Sign, and 10 number digits 0-9, 11
const buckets = Array.from({
length: 11
}, () => []);
// Array
for (let j = 0; j < array.length; j++) {
// -1 for sign, 0 for 0 or no value, and 0-9 for a value
const index = getDigit(array[j], i);
buckets[ascending ? index + 1 : 10 - (index + 1)].push(array[j]);
}
array = buckets.flatMap(item => item);
}
return array;
}
// Move through each letter of the main string, and then move also through all letters of the value, and if all the i + j letters match the j letters of the value in a row, then true, otherwise it doesn’t.
// Complexity: O(n ^ 2)
function naiveSearch(string, value) {
if (value.length > string.length) return false;
for (let i = 0; i < string.length; i++) {
for (let j = 0; j < value.length; j++) {
if (string[i + j] !== value[j]) break;
if (j === value.length - 1) return true;
}
}
return false;
}
// Records at the indexes where the longest prefix exists within the same value, where it ends.
// Complexity: O(n)
function longestPrefixSuffix(value) {
let i = 0;
let j = 1;
const length = value.length;
const lps = new Array(length).fill(0);
while (j < length) {
if (value[i] === value[j]) {
lps[j] = i + 1;
i++;
j++;
} else {
if (i !== 0) i = 0;else j++;
}
}
return lps;
}
// It goes through the string and matches the value sequence, where value match stops in sequence, based on LPS table of the value,
// it doesn’t go back like naive search to i = 1, but it continues from the same i and continues matching from the j at index where prefix repeated position within the value.
// Complexity: O(n)
function kmp(string, value) {
const lengthString = string.length;
const lengthValue = value.length;
const lps = longestPrefixSuffix(value);
let i = 0;
let j = 0;
while (i < lengthString) {
if (string[i] === value[j]) {
i++;
j++;
} else {
if (j > 0) j = lps[j - 1];else i++;
}
if (j === lengthValue) return true;
}
return false;
}
exports.binarySearch = binarySearch;
exports.binarySearchRecursive = binarySearchRecursive;
exports.bubbleSort = bubbleSort;
exports.factorial = factorial;
exports.factorialRecursive = factorialRecursive;
exports.fibonacci = fibonacci;
exports.fibonacciRecursive = fibonacciRecursive;
exports.getDigit = getDigit;
exports.insertionSort = insertionSort;
exports.kmp = kmp;
exports.longestPrefixSuffix = longestPrefixSuffix;
exports.maxDigits = maxDigits;
exports.mergeSort = mergeSort;
exports.mergeSortResolve = mergeSortResolve;
exports.naiveSearch = naiveSearch;
exports.quickSort = quickSort;
exports.radixSort = radixSort;
exports.selectionSort = selectionSort;
Object.defineProperty(exports, '__esModule', { value: true });
}));