z_mirror/flib
git clone https://git.raiguard.me/z_mirror/flib
16ae41b
trunk
1--- Utilities for working with arrays. An array is defined as a table with sequentially numbered keys starting at 1. 2--- Many functions in this module have similar counterparts in the `table` module. In these cases, the array functions 3--- are specifically optimized for arrays and should be used in place of the generic table functions whenever possible. 4--- ```lua 5--- local flib_array = require("__flib__.array") 6--- ``` 7--- @class flib.array 8local flib_array ={} 9 10--- Checks that the predicate is true for all elements of the array. 11--- @generic T 12--- @param array T[] 13--- @param predicate fun(element: T, i: integer): boolean 14--- @return boolean 15function flib_array . all_of ( array , predicate ) 16for i =1 , # array do 17if not predicate ( array [ i ], i ) then 18return false 19end 20end 21return true 22end 23 24--- Checks that the predicate is true for at least one element of the array. 25--- @generic T 26--- @param array T[] 27--- @param predicate fun(element: T, i: integer): boolean 28--- @return boolean 29function flib_array . any_of ( array , predicate ) 30return flib_array . find_if ( array , predicate ) ~= nil 31end 32 33--- Performs a binary search of the array using the given comparator function. The array must be sorted in a manner 34--- compatible with the comparator and must not be modified during the search. The comparator should return `0` if the 35--- element matches the target, a negative number if the element precedes the target, or a positive number if the 36--- element succeeds the target. 37--- 38--- ### Example 39--- 40--- ```lua 41--- local nums = {1, 3, 4, 8, 20, 69} 42--- local looking_for = 20 43--- local i, match = flib_array.binary_search(nums, function(elem) return looking_for - elem end) 44--- assert(i == 5) 45--- assert(match == looking_for) 46--- ``` 47--- @generic T 48--- @param array T[] 49--- @param comparator fun(elem: T): integer 50--- @return integer? The index of the matched element. 51--- @return T? The matched element. 52function flib_array . binary_search ( array , comparator ) 53local low , high =1 , # array 54while low <= high do 55local i =low + math . floor (( high - low ) / 2 ) 56local elem =array [ i ] 57assert ( elem , "Found a nil element during binary search; array was modified or is invalid." ) 58local res =comparator ( elem ) 59if res < 0 then 60high =i - 1 61elseif res > 0 then 62low =i + 1 63else 64return i , array [ i ] 65end 66end 67end 68 69--- Checks whether the array contains the given element. 70--- @generic T 71--- @param array T[] 72--- @param element T 73--- @return boolean 74function flib_array . contains ( array , element ) 75return flib_array . find ( array , element ) ~= nil 76end 77 78--- Shallow compares two arrays for inner equality. Does not compare metatables. 79--- @generic T 80--- @param arr1 T[] 81--- @param arr2 T[] 82--- @return boolean 83function flib_array . eq ( arr1 , arr2 ) 84local len =# arr1 85if len ~= # arr2 then 86return false 87end 88for i =1 , # len do 89if arr1 [ i ] ~= arr2 [ i ] then 90return false 91end 92end 93return true 94end 95 96--- Finds the first index of the given element in the array if it is present. 97--- @generic T 98--- @param array T[] 99--- @param element T 100--- @return integer? 101function flib_array . find ( array , element ) 102for i =1 , # array do 103if array [ i ] == element then 104return i 105end 106end 107end 108 109--- Finds the index of the first element in the array for which the predicate returns true. 110--- @generic T 111--- @param array T[] 112--- @param predicate fun(element: T, i: integer): boolean 113--- @return T? # The element of the match, if any. 114--- @return integer? # The index of the match, if any. 115function flib_array . find_if ( array , predicate ) 116for i =1 , # array do 117local element =array [ i ] 118if predicate ( element , i ) then 119return element , i 120end 121end 122end 123 124--- Flattens the given arrays into a single top-level array. 125--- @generic T 126--- @param arrays T[][] 127--- @return T[] # The merged arrays. 128function flib_array . flatten ( arrays ) 129local output ={} 130local i =0 131for j =1 , # arrays do 132local array =arrays [ j ] 133for k =1 , # array do 134i =i + 1 135output [ i ] =array [ k ] 136end 137end 138return output 139end 140 141--- Transforms the elements of the array in-place according to the predicate. 142--- The predicate MUST NOT return `nil`, as that will introduce a gap and break the array. 143--- To remove elements, use `flib_array.remove_if`. 144--- @generic T, U 145--- @param array T[] 146--- @param predicate fun(element: T, i: integer): U 147function flib_array . map ( array , predicate ) 148for i =1 , # array do 149local new_value =predicate ( array [ i ], i ) 150if new_value == nil then 151error ( 152"Predicate of flib_array.map returned a nil value. To remove elements from an array, use flib_table.remove_if instead." 153) 154end 155array [ i ] =new_value 156end 157end 158 159--- Returns a new array with the elements transformed according to the predicate. 160--- The predicate MUST NOT return `nil`, as that will introduce a gap and break the array. 161--- To remove elements, use `flib_array.remove_if`. 162--- @generic T, U 163--- @param array T[] 164--- @param predicate fun(element: T, i: integer): U 165--- @return U[] 166function flib_array . mapped ( array , predicate ) 167local output ={} 168for i =1 , # array do 169local new_value =predicate ( array [ i ], i ) 170if new_value == nil then 171error ( "Predicate of flib_array.mapped returned a nil value." ) 172end 173output [ i ] =new_value 174end 175return output 176end 177 178--- Removes all elements from the array for which the predicate returns true. 179--- The array will be iterated in reverse order. 180--- @generic T 181--- @param array T[] 182--- @param predicate fun(element: T, i: integer): boolean 183function flib_array . remove_if ( array , predicate ) 184for i =# array , 1 , - 1 do 185if predicate ( array [ i ], i ) then 186table . remove ( array , i ) 187end 188end 189end 190 191--- Shallow copies an array. 192--- @generic T 193--- @param array T[] 194--- @return T[] # The copied array. 195function flib_array . shallow_copy ( array ) 196local new_arr ={} 197for i =1 , # array do 198new_arr [ i ] =array [ i ] 199end 200return new_arr 201end 202 203--- Returns a shallow copy of a portion of an array, selected from `start` to `end` inclusive. 204--- 205--- The original array **will not** be modified. 206--- 207--- ### Examples 208--- 209--- ```lua 210--- local arr = {10, 20, 30, 40, 50, 60, 70, 80, 90} 211--- local sliced = table.slice(arr, 3, 7) -- {30, 40, 50, 60, 70} 212--- log(serpent.line(arr)) -- {10, 20, 30, 40, 50, 60, 70, 80, 90} (unchanged) 213--- ``` 214--- @generic T 215--- @param array T[] 216--- @param start number? default: `1` 217--- @param stop number? Stop at this index. If zero or negative, will stop `n` items from the end of the array (default: `#arr`). 218--- @return T[] # A new array with the copied values. 219function flib_array . slice ( array , start , stop ) 220local output ={} 221local n =# array 222 223start =start or 1 224stop =stop or n 225stop =stop <= 0 and ( n + stop ) or stop 226 227if start < 1 or start > n then 228return {} 229end 230 231local k =1 232for i =start , stop do 233output [ k ] =array [ i ] 234k =k + 1 235end 236return output 237end 238 239--- Excises and returns a portion of an array, selected from `start` to `end` inclusive. 240-- 241--- The original array **will** be modified. 242--- 243--- ### Examples 244--- 245--- ```lua 246--- local arr = {10, 20, 30, 40, 50, 60, 70, 80, 90} 247--- local spliced = table.splice(arr, 3, 7) -- {30, 40, 50, 60, 70} 248--- log(serpent.line(arr)) -- {10, 20, 80, 90} (values were removed) 249--- ``` 250--- @generic T 251--- @param array T[] 252--- @param start number default: `1` 253--- @param stop number? Stop at this index. If zero or negative, will stop `n` items from the end of the array (default: `#arr`). 254--- @return T[] # A new array with the extracted values. 255function flib_array . splice ( array , start , stop ) 256local output ={} 257local n =# array 258 259start =start or 1 260stop =stop or n 261stop =stop <= 0 and ( n + stop ) or stop 262 263if start < 1 or start > n then 264return {} 265end 266 267local k =1 268for _ =start , stop do 269output [ k ] =table . remove ( array , start ) 270k =k + 1 271end 272return output 273end 274 275return flib_array