z_mirror/flib

git clone https://git.raiguard.me/z_mirror/flib

Caleb HeuerRemove useless assert16ae41b

trunk
7.7 KiB275 linesraw
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)
16  for i = 1, #array do
17    if not predicate(array[i], i) then
18      return false
19    end
20  end
21  return 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)
30  return 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)
53  local low, high = 1, #array
54  while low <= high do
55    local i = low + math.floor((high - low) / 2)
56    local elem = array[i]
57    assert(elem, "Found a nil element during binary search; array was modified or is invalid.")
58    local res = comparator(elem)
59    if res < 0 then
60      high = i - 1
61    elseif res > 0 then
62      low = i + 1
63    else
64      return i, array[i]
65    end
66  end
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)
75  return 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)
84  local len = #arr1
85  if len ~= #arr2 then
86    return false
87  end
88  for i = 1, #len do
89    if arr1[i] ~= arr2[i] then
90      return false
91    end
92  end
93  return 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)
102  for i = 1, #array do
103    if array[i] == element then
104      return i
105    end
106  end
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)
116  for i = 1, #array do
117    local element = array[i]
118    if predicate(element, i) then
119      return element, i
120    end
121  end
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)
129  local output = {}
130  local i = 0
131  for j = 1, #arrays do
132    local array = arrays[j]
133    for k = 1, #array do
134      i = i + 1
135      output[i] = array[k]
136    end
137  end
138  return 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)
148  for i = 1, #array do
149    local new_value = predicate(array[i], i)
150    if new_value == nil then
151      error(
152        "Predicate of flib_array.map returned a nil value. To remove elements from an array, use flib_table.remove_if instead."
153      )
154    end
155    array[i] = new_value
156  end
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)
167  local output = {}
168  for i = 1, #array do
169    local new_value = predicate(array[i], i)
170    if new_value == nil then
171      error("Predicate of flib_array.mapped returned a nil value.")
172    end
173    output[i] = new_value
174  end
175  return 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)
184  for i = #array, 1, -1 do
185    if predicate(array[i], i) then
186      table.remove(array, i)
187    end
188  end
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)
196  local new_arr = {}
197  for i = 1, #array do
198    new_arr[i] = array[i]
199  end
200  return 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)
220  local output = {}
221  local n = #array
222
223  start = start or 1
224  stop = stop or n
225  stop = stop <= 0 and (n + stop) or stop
226
227  if start < 1 or start > n then
228    return {}
229  end
230
231  local k = 1
232  for i = start, stop do
233    output[k] = array[i]
234    k = k + 1
235  end
236  return 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)
256  local output = {}
257  local n = #array
258
259  start = start or 1
260  stop = stop or n
261  stop = stop <= 0 and (n + stop) or stop
262
263  if start < 1 or start > n then
264    return {}
265  end
266
267  local k = 1
268  for _ = start, stop do
269    output[k] = table.remove(array, start)
270    k = k + 1
271  end
272  return output
273end
274
275return flib_array