--[[ https://github.com/overextended/ox_lib This file is licensed under LGPL-3.0 or higher Copyright © 2025 Linden ]] ---@class Array : OxClass, { [number]: T } lib.array = lib.class('Array') local table_unpack = table.unpack local table_remove = table.remove local table_clone = table.clone local table_concat = table.concat local table_type = table.type ---@alias ArrayLike Array | { [number]: T } ---@private function lib.array:constructor(...) local arr = { ... } for i = 1, #arr do self[i] = arr[i] end end ---@private function lib.array:__newindex(index, value) if type(index) ~= 'number' then error(("Cannot insert non-number index '%s' into an array."):format(index)) end rawset(self, index, value) end ---Creates a new array from an iteratable value. ---@param iter table | function | string ---@return Array function lib.array:from(iter) local iterType = type(iter) if iterType == 'table' then return lib.array:new(table_unpack(iter)) end if iterType == 'string' then return lib.array:new(string.strsplit('', iter)) end if iterType == 'function' then local arr = lib.array:new() local length = 0 for value in iter do length += 1 arr[length] = value end return arr end error(('Array.from argument was not a valid iterable value (received %s)'):format(iterType)) end ---Returns the element at the given index, with negative numbers counting backwards from the end of the array. ---@param index number ---@return unknown function lib.array:at(index) if index < 0 then index = #self + index + 1 end return self[index] end ---Create a new array containing the elements of two or more arrays. ---@param ... ArrayLike function lib.array:merge(...) local newArr = table_clone(self) local length = #self local arrays = { ... } for i = 1, #arrays do local arr = arrays[i] for j = 1, #arr do length += 1 newArr[length] = arr[j] end end return lib.array:new(table_unpack(newArr)) end ---Tests if all elements in an array succeed in passing the provided test function. ---@param testFn fun(element: unknown): boolean function lib.array:every(testFn) for i = 1, #self do if not testFn(self[i]) then return false end end return true end ---Sets all elements within a range to the given value and returns the modified array. ---@param value any ---@param start? number ---@param endIndex? number function lib.array:fill(value, start, endIndex) local length = #self start = start or 1 endIndex = endIndex or length if start < 1 then start = 1 end if endIndex > length then endIndex = length end for i = start, endIndex do self[i] = value end return self end ---Creates a new array containing the elements from an array that pass the test of the provided function. ---@param testFn fun(element: unknown): boolean function lib.array:filter(testFn) local newArr = {} local length = 0 for i = 1, #self do local element = self[i] if testFn(element) then length += 1 newArr[length] = element end end return lib.array:new(table_unpack(newArr)) end ---Returns the first or last element of an array that passes the provided test function. ---@param testFn fun(element: unknown): boolean ---@param last? boolean function lib.array:find(testFn, last) local a = last and #self or 1 local b = last and 1 or #self local c = last and -1 or 1 for i = a, b, c do local element = self[i] if testFn(element) then return element end end end ---Returns the first or last index of the first element of an array that passes the provided test function. ---@param testFn fun(element: unknown): boolean ---@param last? boolean function lib.array:findIndex(testFn, last) local a = last and #self or 1 local b = last and 1 or #self local c = last and -1 or 1 for i = a, b, c do local element = self[i] if testFn(element) then return i end end end ---Returns the first or last index of the first element of an array that matches the provided value. ---@param value unknown ---@param last? boolean function lib.array:indexOf(value, last) local a = last and #self or 1 local b = last and 1 or #self local c = last and -1 or 1 for i = a, b, c do local element = self[i] if element == value then return i end end end ---Executes the provided function for each element in an array. ---@param cb fun(element: unknown) function lib.array:forEach(cb) for i = 1, #self do cb(self[i]) end end ---Determines if a given element exists inside an array. ---@param element unknown The value to find in the array. ---@param fromIndex? number The position in the array to begin searching from. function lib.array:includes(element, fromIndex) for i = (fromIndex or 1), #self do if self[i] == element then return true end end return false end ---Concatenates all array elements into a string, seperated by commas or the specified seperator. ---@param seperator? string function lib.array:join(seperator) return table_concat(self, seperator or ',') end ---Create a new array containing the results from calling the provided function on every element in an array. ---@param cb fun(element: unknown, index: number, array: self): unknown function lib.array:map(cb) local arr = {} for i = 1, #self do arr[i] = cb(self[i], i, self) end return lib.array:new(table_unpack(arr)) end ---Removes the last element from an array and returns the removed element. function lib.array:pop() return table_remove(self) end ---Adds the given elements to the end of an array and returns the new array length. ---@param ... any function lib.array:push(...) local elements = { ... } local length = #self for i = 1, #elements do length += 1 self[length] = elements[i] end return length end ---The "reducer" function is applied to every element within an array, with the previous element's result serving as the accumulator. ---If an initial value is provided, it's used as the accumulator for index 1; otherwise, index 1 itself serves as the initial value, and iteration begins from index 2. ---@generic T ---@param reducer fun(accumulator: T, currentValue: T, index?: number): T ---@param initialValue? T ---@param reverse? boolean Iterate over the array from right-to-left. ---@return T function lib.array:reduce(reducer, initialValue, reverse) local length = #self local initialIndex = initialValue and 1 or 2 local accumulator = initialValue or self[1] if reverse then for i = initialIndex, length do local index = length - i + initialIndex accumulator = reducer(accumulator, self[index], index) end else for i = initialIndex, length do accumulator = reducer(accumulator, self[i], i) end end return accumulator end ---Reverses the elements inside an array. function lib.array:reverse() local i, j = 1, #self while i < j do self[i], self[j] = self[j], self[i] i += 1 j -= 1 end return self end ---Removes the first element from an array and returns the removed element. function lib.array:shift() return table_remove(self, 1) end ---Creates a shallow copy of a portion of an array as a new array. ---@param start? number ---@param finish? number function lib.array:slice(start, finish) local length = #self start = start or 1 finish = finish or length if start < 0 then start = length + start + 1 end if finish < 0 then finish = length + finish + 1 end if start < 1 then start = 1 end if finish > length then finish = length end local arr = lib.array:new() local index = 0 for i = start, finish do index += 1 arr[index] = self[i] end return arr end ---Creates a new array with reversed elements from the given array. function lib.array:toReversed() local reversed = lib.array:new() for i = #self, 1, -1 do reversed:push(self[i]) end return reversed end ---Inserts the given elements to the start of an array and returns the new array length. ---@param ... any function lib.array:unshift(...) local elements = { ... } local length = #self local eLength = #elements for i = length, 1, -1 do self[i + eLength] = self[i] end for i = 1, #elements do self[i] = elements[i] end return length + eLength end ---Returns true if the given table is an instance of array or an array-like table. ---@param tbl ArrayLike ---@return boolean function lib.array.isArray(tbl) local tableType = table_type(tbl) if not tableType then return false end if tableType == 'array' or tableType == 'empty' or lib.array.instanceOf(tbl, lib.array) then return true end return false end return lib.array