diff --git a/README.md b/README.md index 0e82cf8aa..6ebded7f7 100644 --- a/README.md +++ b/README.md @@ -70,20 +70,18 @@ xarray.permutations([1, 2, 3]); | Property | Description | | ---- | ---- | -| [fromIterable] | Convert an iterable to array. | -| [fromIterable$] | Convert an iterable to array! | | [fromRange] | Generate array from given number range. | | [fromInvocation] | Generate array from repeated function invocation. | | [fromApplication] | Generate array from repeated function application. | +| [fromIterable] | Convert an iterable to array. | +| [fromIterable$] | Convert an iterable to array! | | | | | [shallowClone] | Shallow clone an array. | | [deepClone] | Deep clone an array. | | | | | [is] | Check if value is an array. | -| [isSorted] | Examine if array is sorted. | | [keys] | Obtain all indices. | | [values] | Get all values. | -| [values$] | Get all values! | | [entries] | Obtain all index-value pairs. | | [ikeys] | List all indices. | | [ivalues] | List all values. | @@ -91,8 +89,9 @@ xarray.permutations([1, 2, 3]); | | | | [index] | Get zero-based index for an element in array. | | [indexRange] | Get zero-based index range for part of array. | -| [length] | Find the length of an array. | +| | | | [isEmpty] | Check if an array is empty. | +| [length] | Find the length of an array. | | [resize$] | Resize an array to given length! | | [clear$] | Remove all elements from an array! | | | | @@ -111,17 +110,25 @@ xarray.permutations([1, 2, 3]); | [remove$] | Remove value at index! | | [removePath$] | Remove value at path in a nested array! | | | | -| [count] | Count values which satisfy a test. | -| [countEach] | Count occurrences of each distinct value. | -| [min] | Find first smallest value. | -| [minEntry] | Find first smallest entry. | -| [max] | Find first largest value. | -| [maxEntry] | Find first largest entry. | +| [isSorted] | Examine if array is sorted. | +| [hasUnsortedValue] | Examine if array has an unsorted value. | +| [searchUnsortedValue] | Find first index of an unsorted value. | +| [sort] | Arrange values in order. | +| [sort$] | Arrange values in order! | +| [partialSort] | Partially arrange values in order. | +| [partialSort$] | Partially arrange values in order! | +| | | +| [minimum] | Find first smallest value. | +| [minimumEntry] | Find first smallest entry. | +| [maximum] | Find first largest value. | +| [maximumEntry] | Find first largest entry. | | [range] | Find smallest and largest values. | | [rangeEntries] | Find smallest and largest entries. | +| [searchMinimumValue] | Find first index of minimum value. | +| [searchMaximumValue] | Find first index of maximum value. | | | | -| [compare] | Compare two arrays (lexicographically). | | [isEqual] | Examine if two arrays are equal. | +| [compare] | Compare two arrays (lexicographically). | | | | | [head] | Get first value. | | [last] | Get last value. | @@ -130,6 +137,10 @@ xarray.permutations([1, 2, 3]); | [init] | Get values except last. | | [slice] | Get part of an array. | | [slice$] | Get part of an array! | +| | | +| [find] | Find first value passing a test. | +| [findRight] | Find last value passing a test. | +| | | | [take] | Keep first n values only. | | [takeRight] | Keep last n values only. | | [takeWhile] | Keep values from left, while a test passes. | @@ -139,55 +150,52 @@ xarray.permutations([1, 2, 3]); | [dropWhile] | Discard values from left, while a test passes. | | [dropWhileRight] | Discard values from right, while a test passes. | | | | -| [prefixes] | Obtain all possible prefixes. | -| [suffixes] | Obtain all possible suffixes. | -| [infixes] | Obtain all possible infixes. | -| [subsequences] | Obtain all possible subsequences. | -| [permutations] | Obtain all possible permutations. | -| [iprefixes] | List all possible prefixes. | -| [isuffixes] | List all possible suffixes. | -| [iinfixes] | List all possible infixes. | -| [isubsequences] | List all possible subsequences. | -| [ipermutations] | List all possible permutations. | -| [randomValue] | Pick an arbitrary value. | -| [randomPrefix] | Pick an arbitrary prefix. | -| [randomSuffix] | Pick an arbitrary suffix. | -| [randomInfix] | Pick an arbitrary infix. | -| [randomSubsequence] | Pick an arbitrary subsequence. | -| [randomPermutation] | Pick an arbitrary permutation. | -| [randomPermutation$] | Pick an arbitrary permutation! | -| | | -| [includes] | Check if array has a value. | -| [indexOf] | Find first index of a value. | -| [lastIndexOf] | Find last index of a value. | -| [find] | Find first value passing a test. | -| [findRight] | Find last value passing a test. | | [scanWhile] | Scan from left, while a test passes. | | [scanWhileRight] | Scan from right, while a test passes. | | [scanUntil] | Scan from left, until a test passes. | | [scanUntilRight] | Scan from right, until a test passes. | +| | | +| [indexOf] | Find first index of a value. | +| [lastIndexOf] | Find last index of a value. | | [search] | Find index of first value passing a test. | | [searchRight] | Find index of last value passing a test. | | [searchAll] | Find indices of values passing a test. | +| | | +| [includes] | Check if array has a value. | +| [hasValue] | Examine if array has a value. | | [searchValue] | Find first index of a value. | | [searchValueRight] | Find last index of a value. | | [searchValueAll] | Find indices of value. | -| [searchMinimumValue] | Find first index of minimum value. | -| [searchMaximumValue] | Find first index of maximum value. | -| [searchUnsortedValue] | Find first index of an unsorted value. | | [searchAdjacentDuplicateValue] | Find first index of an adjacent duplicate value. | | [searchMismatchedValue] | Find first index where two arrays differ. | -| [searchInfix] | Find first index of an infix. | -| [searchInfixRight] | Find last index of an infix. | -| [searchInfixAll] | Find indices of an infix. | -| [searchSubsequence] | Find first index of a subsequence. | -| [hasValue] | Examine if array has a value. | -| [hasUnsortedValue] | Examine if array has an unsorted value. | +| | | | [hasPrefix] | Examine if array starts with a prefix. | | [hasSuffix] | Examine if array ends with a suffix. | | [hasInfix] | Examine if array contains an infix. | | [hasSubsequence] | Examine if array has a subsequence. | | [hasPermutation] | Examine if array has a permutation. | +| [prefixes] | Obtain all possible prefixes. | +| [suffixes] | Obtain all possible suffixes. | +| [infixes] | Obtain all possible infixes. | +| [subsequences] | Obtain all possible subsequences. | +| [permutations] | Obtain all possible permutations. | +| [iprefixes] | List all possible prefixes. | +| [isuffixes] | List all possible suffixes. | +| [iinfixes] | List all possible infixes. | +| [isubsequences] | List all possible subsequences. | +| [ipermutations] | List all possible permutations. | +| [searchInfix] | Find first index of an infix. | +| [searchInfixRight] | Find last index of an infix. | +| [searchInfixAll] | Find indices of an infix. | +| [searchSubsequence] | Find first index of a subsequence. | +| | | +| [randomValue] | Pick an arbitrary value. | +| [randomPrefix] | Pick an arbitrary prefix. | +| [randomSuffix] | Pick an arbitrary suffix. | +| [randomInfix] | Pick an arbitrary infix. | +| [randomSubsequence] | Pick an arbitrary subsequence. | +| [randomPermutation] | Pick an arbitrary permutation. | +| [randomPermutation$] | Pick an arbitrary permutation! | | | | | [forEach] | Call a function for each value. | | [some] | Examine if any value satisfies a test. | @@ -196,23 +204,24 @@ xarray.permutations([1, 2, 3]); | [map$] | Transform values of an array! | | [reduce] | Reduce values of array to a single value. | | [reduceRight] | Reduce values from right, to a single value. | -| [exclusiveScan] | Perform exclusive prefix scan from left to right. | -| [exclusiveScan$] | Perform exclusive prefix scan from left to right! | -| [inclusiveScan] | Perform inclusive prefix scan from left to right. | -| [inclusiveScan$] | Perform inclusive prefix scan from left to right! | -| [adjacentCombine] | Combine adjacent values of an array. | -| [adjacentCombine$] | Combine adjacent values of an array! | | [filter] | Keep values which pass a test. | | [filter$] | Keep values which pass a test! | | [filterAt] | Keep values at given indices. | | [reject] | Discard values which pass a test. | | [reject$] | Discard values which pass a test! | | [rejectAt] | Discard values at given indices. | -| [accumulate] | Produce accumulating values. | | [flat] | Flatten nested array to given depth. | | [flatMap] | Flatten nested array, based on map function. | | [zip] | Combine values from arrays. | | | | +| [accumulate] | Produce accumulating values. | +| [exclusiveScan] | Perform exclusive prefix scan from left to right. | +| [exclusiveScan$] | Perform exclusive prefix scan from left to right! | +| [inclusiveScan] | Perform inclusive prefix scan from left to right. | +| [inclusiveScan$] | Perform inclusive prefix scan from left to right! | +| [adjacentCombine] | Combine adjacent values of an array. | +| [adjacentCombine$] | Combine adjacent values of an array! | +| | | | [fill] | Fill with given value. | | [fill$] | Fill with given value! | | [push] | Add value to the end. | @@ -231,6 +240,12 @@ xarray.permutations([1, 2, 3]); | [moveWithin$] | Move part of array within! | | [splice] | Remove or replace existing values. | | [splice$] | Remove or replace existing values! | +| | | +| [count] | Count values which satisfy a test. | +| [countEach] | Count occurrences of each distinct value. | +| [partition] | Segregate values by test result. | +| [partitionEach] | Segregate each distinct value. | +| | | | [split] | Break array considering test as separator. | | [splitAt] | Break array considering indices as separator. | | [cut] | Break array when test passes. | @@ -238,8 +253,6 @@ xarray.permutations([1, 2, 3]); | [cutAt] | Break array at given indices. | | [cutAtRight] | Break array after given indices. | | [group] | Keep similar values together and in order. | -| [partition] | Segregate values by test result. | -| [partitionEach] | Segregate each distinct value. | | [chunk] | Break array into chunks of given size. | | [cycle] | Obtain values that cycle through array. | | [repeat] | Repeat an array given times. | @@ -247,6 +260,7 @@ xarray.permutations([1, 2, 3]); | [reverse$] | Reverse the values! | | [rotate] | Rotate values in array. | | [rotate$] | Rotate values in array! | +| | | | [intersperse] | Place a separator between every value. | | [interpolate] | Estimate new values between existing ones. | | [intermix] | Place values of an array between another. | @@ -265,11 +279,6 @@ xarray.permutations([1, 2, 3]); | [difference] | Obtain values not present in another array. | | [symmetricDifference] | Obtain values not present in both arrays. | | [cartesianProduct] | Obtain cartesian product of arrays. | -| | | -| [sort] | Arrange values in order. | -| [sort$] | Arrange values in order! | -| [partialSort] | Partially arrange values in order. | -| [partialSort$] | Partially arrange values in order! |

@@ -324,10 +333,10 @@ xarray.permutations([1, 2, 3]); [removePath$]: https://github.com/nodef/extra-array/wiki/removePath$ [count]: https://github.com/nodef/extra-array/wiki/count [countEach]: https://github.com/nodef/extra-array/wiki/countEach -[min]: https://github.com/nodef/extra-array/wiki/min -[minEntry]: https://github.com/nodef/extra-array/wiki/minEntry -[max]: https://github.com/nodef/extra-array/wiki/max -[maxEntry]: https://github.com/nodef/extra-array/wiki/maxEntry +[minimum]: https://github.com/nodef/extra-array/wiki/minimum +[minimumEntry]: https://github.com/nodef/extra-array/wiki/minimumEntry +[maximum]: https://github.com/nodef/extra-array/wiki/maximum +[maximumEntry]: https://github.com/nodef/extra-array/wiki/maximumEntry [range]: https://github.com/nodef/extra-array/wiki/range [rangeEntries]: https://github.com/nodef/extra-array/wiki/rangeEntries [slice]: https://github.com/nodef/extra-array/wiki/slice diff --git a/TODO b/TODO index 580d8dc22..3a3957a93 100644 --- a/TODO +++ b/TODO @@ -1,97 +1,6 @@ -SORTS, IN PAIR - -Instead of mapFn, provide pair array to define sort mapping. -selectionSort, insertionSort, bubbleSort, mergeSort, (are all stable?) -quickSort, shellSort, radixSort, timSort, heapSort -https://en.wikipedia.org/wiki/Sorting_algorithm -https://www.npmjs.com/package/sort-algorithms-js -https://dl.acm.org/doi/10.5555/1778580.1778601 -https://stackoverflow.com/questions/463105/in-place-radix-sort/474040#474040 - - -MINNTH, MAXNTH, SORTEDSLICE (ARRAYVIEW) - -Need only part of sorted array? [bag][nth] ... -Selection algorithm: Quickselect -https://stackoverflow.com/questions/34395680/what-is-the-fastest-way-to-find-nth-biggest-number-of-an-int-array -https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array/ - - PARSE, OTHERS - (fm, fc)? INDEX -VE - moveWithin - - -OLD SORT ALGORITHMS - -/** - * Arrange values in order! - * @param x an array (updated!) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function bubbleSort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; // TODO: use map function - var fs = fs || swap$; - var X = x.length; - for (var i=0; i 0) fs(x, i, j); - } - return x; -} - - -/** - * Arrange values in order! - * @param x an array (updated!) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function selectionSort$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; // TODO: use map function - var fs = fs || swap$; - var X = x.length; - for (var i=0; i 0) l = j; - fs(x, i, l); - } - return x; -} - - -// TODO: Check if this is correct! -/** - * Arrange values in order! - * @param x an array (updated!) - * @param fc compare function (a, b) - * @param fm map function (v, i, x) - * @param fs swap function (x, i, j) - * @returns x | x[i] ≤ x[j] ∀ i ≤ j - */ -function insertionSortOld$(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null, fs: SwapFunction | null=null): T[] { - var fc = fc || COMPARE; - var fm = fm || IDENTITY; // TODO: use map function - var fs = fs || swap$; // TODO: use swap function - var X = x.length; - for (var i=X-2; i>=0; --i) { - var xv = x[i], mv = x[i]; - for (var j=i+1; j(x: T[]): T[] { } -/** - * Get all values! - * @param x an array - * @returns x (reference!) - */ -export function values$(x: T[]): T[] { - return x; -} - - /** * List all values. * @param x an array @@ -673,10 +663,11 @@ export {countEach as countAs}; // DEPRECATED * @param fm map function (v, i, x) * @returns v | v ≤ vᵢ; vᵢ ∈ x */ -export function min(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { +export function minimum(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { var i = searchMinimumValue(x, fc, fm); return x[i]; } +export {minimum as min}; /** @@ -686,10 +677,11 @@ export function min(x: T[], fc: CompareFunction | null=null, fm: Ma * @param fm map function (v, i, x) * @returns [min_index, min_value] */ -export function minEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { +export function minimumEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { var i = searchMinimumValue(x, fc, fm); return [i, x[i]]; } +export {minimumEntry as minEntry}; /** @@ -699,10 +691,11 @@ export function minEntry(x: T[], fc: CompareFunction | null=null, f * @param fm map function (v, i, x) * @returns v | v ≥ vᵢ; vᵢ ∈ x */ -export function max(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { +export function maximum(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): T { var i = searchMaximumValue(x, fc, fm); return x[i]; } +export {maximum as max}; /** @@ -712,10 +705,11 @@ export function max(x: T[], fc: CompareFunction | null=null, fm: Ma * @param fm map function (v, i, x) * @returns [max_index, max_value] */ -export function maxEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { +export function maximumEntry(x: T[], fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T] { var i = searchMaximumValue(x, fc, fm); return [i, x[i]]; } +export {maximumEntry as maxEntry}; /** @@ -753,6 +747,62 @@ export function rangeEntries(x: T[], fc: CompareFunction | null=nul } return [[mi, mv], [ni, nv]]; } + + +/** + * Find smallest values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n smallest values in ascending order + */ +export function minimums(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { + var is = searchMinimumValues(x, n, fc, fm); + return is.map(i => x[i]); +} + + +/** + * Find smallest entries. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n smallest entries in ascending order + */ +export function minimumEntries(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T][] { + var is = searchMinimumValues(x, n, fc, fm); + return is.map(i => [i, x[i]]); +} + + +/** + * Find largest values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n largest values in descending order + */ +export function maximums(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): T[] { + var is = searchMaximumValues(x, n, fc, fm); + return is.map(i => x[i]); +} + + +/** + * Find largest entries. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns n largest entries in descending order + */ +export function maximumEntries(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): [number, T][] { + var is = searchMaximumValues(x, n, fc, fm); + return is.map(i => [i, x[i]]); +} // #endregion @@ -1498,6 +1548,52 @@ export function searchMaximumValue(x: T[], fc: CompareFunction | nu } +/** + * Find indices of minimum values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns indices of minimum values in ascending order + */ +export function searchMinimumValues(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): number[] { + var fc = fc || COMPARE; + var fm = fm || IDENTITY; + var X = x.length; + // Create a max heap of indices. + var IH = Math.min(n, X); + var ih = fromRange(0, IH); + buildMaxHeap$(ih, 0, IH, fc, i => fm(x[i], i, x), swapRaw$); + var wr = fm(x[ih[0]], ih[0], x); + // Search for minimum values, and update heap. + for (var i=n; i= 0) continue; + ih[0] = i; + maxHeapify$(ih, 0, IH, 0, fc, i => fm(x[i], i, x), swapRaw$); + var wr = fm(x[ih[0]], ih[0], x); + } + // Sort max heap in ascending order. + ih.sort((i, j) => fc(fm(x[i], i, x), fm(x[j], j, x))); + return ih; +} + + +/** + * Find indices of maximum values. + * @param x an array + * @param n number of values + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @returns indices of maximum values in descending order + */ +export function searchMaximumValues(x: T[], n: number, fc: CompareFunction | null=null, fm: MapFunction | null=null): number[] { + var fc = fc || COMPARE; + var fd = (a: T|U, b: T|U) => -fc(a, b); + return searchMinimumValues(x, n, fd, fm); +} + + /** * Find first index of an unsorted value. * @param x an array @@ -3272,8 +3368,8 @@ function buildReverseMinHeap$(x: T[], i: number, I: number, fc: CompareF */ function reverseMinHeapify$(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { var s = r; // Index of smallest value - var lt = 2*r - I; // Reverse of lt = 2*r+1 - var rt = lt - 1; // Reverse of rt = 2*r+2 + var lt = 2*r - I; // Left child, reverse of lt = 2*r+1 + var rt = lt - 1; // Right child, reverse of rt = 2*r+2 if (lt>=i && fc(fm(x[lt], lt, x), fm(x[s], s, x)) < 0) s = lt; // Left child is smaller? if (rt>=i && fc(fm(x[rt], rt, x), fm(x[s], s, x)) < 0) s = rt; // Right child is smaller? if (s !== r) { // Smallest is not root? @@ -3283,6 +3379,36 @@ function reverseMinHeapify$(x: T[], i: number, I: number, r: number, fc: } +// Build a max-heap, where root node is the smallest and placed at the beginning. +function buildMaxHeap$(x: T[], i: number, I: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + for (var r=i+Math.floor((I-i)/2)-1; r>=i; --r) + maxHeapify$(x, i, I, r, fc, fm, fs); +} + + +/** + * Max-heapify values, such that root node is the largest and placed at the beginning. + * @param x an array (updated!) + * @param i start index + * @param I end index (exclusive) + * @param r root index + * @param fc compare function (a, b) + * @param fm map function (v, i, x) + * @param fs swap function (x, i, j) + */ +function maxHeapify$(x: T[], i: number, I: number, r: number, fc: CompareFunction, fm: MapFunction, fs: SwapFunction): void { + var s = r; // Index of largest value + var lt = 2*r - i + 1; // Left child, like lt = 2*r+1 + var rt = lt + 1; // Right child, like rt = 2*r+2 + if (lt 0) s = lt; // Left child is larger? + if (rt 0) s = rt; // Right child is larger? + if (s !== r) { // Largest is not root? + fs(x, s, r); // Swap root with largest + maxHeapify$(x, i, I, s, fc, fm, fs); // Rebuild heap + } +} + + /** * Partially arrange values in order! * @param x an array (updated!) diff --git a/tests/index.test.ts b/tests/index.test.ts index 480c55646..1d2b4c8ff 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -76,6 +76,7 @@ import { searchValue, searchValueRight, searchValueAll, + searchMinimumValues, searchInfix, searchInfixRight, searchInfixAll, @@ -983,6 +984,13 @@ test("searchValueAll", () => { }); +test("searchMinimumValues", () => { + var x = [2, 5, 3, 1, 4]; + var a = searchMinimumValues(x, 3); + expect(a).toStrictEqual([3, 0, 2]); +}); + + test("searchInfix", () => { var x = [1, 2, 3, 4]; var y = [2, 3]; diff --git a/wiki b/wiki index 1706a1f2f..9d7dc04ca 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 1706a1f2fa91905fb0ffd79900a065aee3ea20a7 +Subproject commit 9d7dc04cae2ac58736d840137a630632dfc18c1e