z_mirror/flib

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

Caleb HeuerSplit table and array modules and massively refactor table module741edfb

trunk
8.1 KiB300 linesraw
1--- Additional utilities for working with tables. For arrays, see the `array` module.
2--- ```lua
3--- local flib_table: = require("__flib__.table")
4--- ```
5--- @class flib_table
6local flib_table = {}
7
8--- Checks that the predicate is true for all values of the table.
9--- @generic K, V
10--- @param tbl table<K, V>
11--- @param predicate fun(value: V, key: K): boolean
12--- @return boolean
13function flib_table.all_of(tbl, predicate)
14  for k, v in pairs(tbl) do
15    if not predicate(v, k) then
16      return false
17    end
18  end
19  return true
20end
21
22--- Checks that the predicate is true at least one entry of the table.
23--- @generic K, V
24--- @param tbl table<K, V>
25--- @param predicate fun(value: V, key: K): boolean
26--- @return boolean
27function flib_table.any_of(tbl, predicate)
28  for k, v in pairs(tbl) do
29    if predicate(v, k) then
30      return true
31    end
32  end
33  return false
34end
35
36--- Recursively compares two tables for inner equality. Does not compare metatables.
37--- @generic T: table
38--- @param tbl1 T
39--- @param tbl2 T
40--- @return boolean
41function flib_table.deep_compare(tbl1, tbl2)
42  if tbl1 == tbl2 then
43    return true
44  end
45  for k, v in pairs(tbl1) do
46    if type(v) == "table" and type(tbl2[k]) == "table" then
47      if not flib_table.deep_compare(v, tbl2[k]) then
48        return false
49      end
50    else
51      if v ~= tbl2[k] then
52        return false
53      end
54    end
55  end
56  for k in pairs(tbl2) do
57    if tbl1[k] == nil then
58      return false
59    end
60  end
61  return true
62end
63
64--- Recursively copies the contents of a table into a new table. Does not create new copies of Factorio objects.
65--- @generic T
66--- @param tbl T
67--- @return T
68function flib_table.deep_copy(tbl)
69  local lookup_table = {}
70  local function _copy(tbl)
71    if type(tbl) ~= "table" then
72      return tbl
73    elseif lookup_table[tbl] then
74      return lookup_table[tbl]
75    end
76    local new_table = {}
77    lookup_table[tbl] = new_table
78    for index, value in pairs(tbl) do
79      new_table[_copy(index)] = _copy(value)
80    end
81    return setmetatable(new_table, getmetatable(tbl))
82  end
83  return _copy(tbl)
84end
85
86--- Recursively merges two or more tables.
87---
88--- Values from earlier tables are overwritten by values from later tables, unless both values are tables, in which case
89--- they are recursively merged.
90---
91--- Non-merged tables are deep-copied, so the result is brand-new.
92---
93--- ### Examples
94---
95--- ```lua
96--- local tbl = {foo = "bar"}
97--- log(tbl.foo) -- logs "bar"
98--- log (tbl.bar) -- errors (key is nil)
99--- tbl = table.merge{tbl, {foo = "baz", set = 3}}
100--- log(tbl.foo) -- logs "baz"
101--- log(tbl.set) -- logs "3"
102--- ```
103--- @generic K, V
104--- @param tables table<K, V>[] An array of tables to merge.
105--- @return table<K, V>
106function flib_table.deep_merge(tables)
107  local output = {}
108  for _, tbl in ipairs(tables) do
109    for k, v in pairs(tbl) do
110      if type(v) == "table" then
111        if type(output[k] or false) == "table" then
112          output[k] = flib_table.deep_merge({ output[k], v })
113        else
114          output[k] = flib_table.deep_copy(v)
115        end
116      else
117        output[k] = v
118      end
119    end
120  end
121  return output
122end
123
124--- Finds the first key of the given element in the table. The search order is determined by the `pairs()` function.
125---
126--- ### Examples
127---
128--- ```lua
129--- local tbl = {"foo", "bar"}
130--- local key_of_foo = table.find(tbl, "foo") -- 1
131--- local key_of_baz = table.find(tbl, "baz") -- nil
132--- ```
133--- @generic K, V
134--- @param tbl table<K, V> The table to search.
135--- @param value V The value to match. Must have an `eq` metamethod set, otherwise will error.
136--- @return K? key The first key corresponding to `value`, if any.
137function flib_table.find(tbl, value)
138  for k, v in pairs(tbl) do
139    if v == value then
140      return k
141    end
142  end
143end
144
145--- Retrieves the value at the key, or inserts the default value.
146--- @generic K, V
147--- @param table table<K, V>
148--- @param key K
149--- @param default_value V
150--- @return V
151function flib_table.get_or_insert(table, key, default_value)
152  local value = table[key]
153  if not value then
154    table[key] = default_value
155    return default_value
156  end
157  return value
158end
159
160--- Retrieves the value at the key, or inserts the default value provided by the callback.
161--- @generic K, V
162--- @param table table<K, V>
163--- @param key K
164--- @param callback fun(): V
165--- @return V
166function flib_table.get_or_insert_with(table, key, callback)
167  local value = table[key]
168  if not value then
169    value = callback()
170    table[key] = value
171  end
172  return value
173end
174
175--- Returns a new table with the keys and values of the source table inverted. Non-unique values are overwritten based
176--- on the ordering from `pairs()`.
177---
178--- ### Examples
179---
180--- ```lua
181--- local tbl = {"foo", "bar", "baz", set = "baz"}
182--- local inverted = table.inverted(tbl) -- {foo = 1, bar = 2, baz = "set"}
183--- ```
184--- @generic K, V
185--- @param tbl table<K, V>
186--- @return table<V, K>
187function flib_table.inverted(tbl)
188  local inverted = {}
189  for k, v in pairs(tbl) do
190    inverted[v] = k
191  end
192  return inverted
193end
194
195--- Transforms the values of the table in-place according to the predicate.
196--- @generic K, V, N
197--- @param tbl table<K, V>
198--- @param predicate fun(value: V, key: V): N?
199--- @return table<K, N> # The input table.
200function flib_table.map(tbl, predicate)
201  for k, v in pairs(tbl) do
202    tbl[k] = predicate(v, k)
203  end
204  return tbl
205end
206
207--- Returns a new table with the values transformed according to the predicate.
208---
209--- ### Examples
210---
211--- ```lua
212--- local tbl = {1, 2, 3, 4, 5}
213--- local tbl_times_ten = table.mapped(tbl, function(v) return v * 10 end) -- {10, 20, 30, 40, 50}
214--- ```
215--- @generic K, V, N
216--- @param tbl table<K, V>
217--- @param predicate fun(value: V, key: V): N?
218--- @return table<K, N>
219function flib_table.mapped(tbl, predicate)
220  local output = {}
221  for k, v in pairs(tbl) do
222    output[k] = predicate(v, k)
223  end
224  return output
225end
226
227--- "Reduce" a table's values into a single output value, using the results of a reducer function.
228---
229--- Calls `reducer(accumulator, value, key)` on each element in the table, returning a single accumulated output value.
230---
231--- ### Examples
232---
233--- ```lua
234--- local tbl = {10, 20, 30, 40, 50}
235--- local sum = table.reduce(tbl, function(acc, v) return acc + v end)
236--- local sum_minus_ten = table.reduce(tbl, function(acc, v) return acc + v end, -10)
237--- ```
238--- @generic K, V, R
239--- @param tbl table<K, V>
240--- @param reducer fun(acc: R, value: V, key: K): R
241--- @param initial_value R? The initial value for the accumulator. If not provided or is falsy, the first value in the table will be used as the initial `accumulator` value and skipped as `key`. Calling `reduce()` on an empty table without an `initial_value` will cause a crash.
242--- @return R
243function flib_table.reduce(tbl, reducer, initial_value)
244  local accumulator = initial_value
245  for key, value in pairs(tbl) do
246    if accumulator then
247      accumulator = reducer(accumulator, value, key)
248    else
249      accumulator = value
250    end
251  end
252  return accumulator
253end
254
255--- Removes all elements from the table for which the predicate returns true.
256--- @generic K, V
257--- @param tbl table<K, V>
258--- @param predicate fun(value: V, key: K): boolean
259function flib_table.remove_if(tbl, predicate)
260  for k, v in pairs(tbl) do
261    if predicate(v, k) then
262      tbl[k] = nil
263    end
264  end
265end
266
267--- Shallowly copies the contents of a table into a new table.
268--- The parent table will have a new table reference, but any subtables within it will still have the same table reference.
269--- Does not copy metatables.
270--- @generic T
271--- @param tbl T
272--- @param use_rawset boolean? Use rawset to set the values (ignores metamethods).
273--- @return T The copied table.
274function flib_table.shallow_copy(tbl, use_rawset)
275  local output = {}
276  for k, v in pairs(tbl) do
277    if use_rawset then
278      rawset(output, k, v)
279    else
280      output[k] = v
281    end
282  end
283  return output
284end
285
286--- Shallowly merge two or more tables.
287--- Unlike `table.deep_merge`, this will only combine the top level of the tables.
288--- @param tables table[]
289--- @return table
290function flib_table.shallow_merge(tables)
291  local output = {}
292  for _, tbl in pairs(tables) do
293    for key, value in pairs(tbl) do
294      output[key] = value
295    end
296  end
297  return output
298end
299
300return flib_table