1 | -------------------------------------------------------------------------------- |
2 | --- Small table utilities |
3 | -- @module lua-nucleo.table-utils |
4 | -- This file is a part of lua-nucleo library |
5 | -- @copyright lua-nucleo authors (see file `COPYRIGHT` for the license) |
6 | -------------------------------------------------------------------------------- |
7 | |
8 | local setmetatable, error, pairs, ipairs, tostring, select, type, assert |
9 | = setmetatable, error, pairs, ipairs, tostring, select, type, assert |
10 | |
11 | local rawget = rawget |
12 | |
13 | local table_insert, table_remove = table.insert, table.remove |
14 | |
15 | local math_min, math_max = math.min, math.max |
16 | |
17 | -------------------------------------------------------------------------------- |
18 | |
19 | local arguments, |
20 | optional_arguments, |
21 | method_arguments |
22 | = import 'lua-nucleo/args.lua' |
23 | { |
24 | 'arguments', |
25 | 'optional_arguments', |
26 | 'method_arguments' |
27 | } |
28 | |
29 | local is_number, |
30 | is_table |
31 | = import 'lua-nucleo/type.lua' |
32 | { |
33 | 'is_number', |
34 | 'is_table' |
35 | } |
36 | |
37 | local assert_is_table |
38 | = import 'lua-nucleo/typeassert.lua' |
39 | { |
40 | 'assert_is_table' |
41 | } |
42 | |
43 | -------------------------------------------------------------------------------- |
44 | |
45 | -- Warning: it is possible to corrupt this with rawset and debug.setmetatable. |
46 | local empty_table = setmetatable( |
47 | { }, |
48 | { |
49 | __newindex = function(t, k, v) |
50 | error("attempted to change the empty table", 2) |
51 | end; |
52 | |
53 | __metatable = "empty_table"; |
54 | } |
55 | ) |
56 | |
57 | local function toverride_many(t, s, ...) |
58 | if s then |
59 | for k, v in pairs(s) do |
60 | t[k] = v |
61 | end |
62 | -- Recursion is usually faster than calling select() |
63 | return toverride_many(t, ...) |
64 | end |
65 | |
66 | return t |
67 | end |
68 | |
69 | local function tappend_many(t, s, ...) |
70 | if s then |
71 | for k, v in pairs(s) do |
72 | if t[k] == nil then |
73 | t[k] = v |
74 | else |
75 | error("attempted to override table key `" .. tostring(k) .. "'", 2) |
76 | end |
77 | end |
78 | |
79 | -- Recursion is usually faster than calling select() |
80 | return tappend_many(t, ...) |
81 | end |
82 | |
83 | return t |
84 | end |
85 | |
86 | local function tijoin_many(t, s, ...) |
87 | if s then |
88 | -- Note: can't use ipairs() since we want to support tijoin_many(t, t) |
89 | for i = 1, #s do |
90 | t[#t + 1] = s[i] |
91 | end |
92 | |
93 | -- Recursion is usually faster than calling select() |
94 | return tijoin_many(t, ...) |
95 | end |
96 | |
97 | return t |
98 | end |
99 | |
100 | -- Keys are ordered in undetermined order |
101 | local tkeys = function(t) |
102 | local r = { } |
103 | |
104 | for k, v in pairs(t) do |
105 | r[#r + 1] = k |
106 | end |
107 | |
108 | return r |
109 | end |
110 | |
111 | -- Values are ordered in undetermined order |
112 | local tvalues = function(t) |
113 | local r = { } |
114 | |
115 | for k, v in pairs(t) do |
116 | r[#r + 1] = v |
117 | end |
118 | |
119 | return r |
120 | end |
121 | |
122 | -- Keys and values are ordered in undetermined order |
123 | local tkeysvalues = function(t) |
124 | local keys, values = { }, { } |
125 | |
126 | for k, v in pairs(t) do |
127 | keys[#keys + 1] = k |
128 | values[#values + 1] = v |
129 | end |
130 | |
131 | return keys, values |
132 | end |
133 | |
134 | -- If table contains multiple keys with the same value, |
135 | -- only one key is stored in the result, picked in undetermined way. |
136 | local tflip = function(t) |
137 | local r = { } |
138 | |
139 | for k, v in pairs(t) do |
140 | r[v] = k |
141 | end |
142 | |
143 | return r |
144 | end |
145 | |
146 | -- If table contains multiple keys with the same value, |
147 | -- only one key is stored in the result, picked in undetermined way. |
148 | local tflip_inplace = function(t) |
149 | for k, v in pairs(t) do |
150 | t[v] = k |
151 | end |
152 | |
153 | return t |
154 | end |
155 | |
156 | -- If table contains multiple keys with the same value, |
157 | -- only the last such key (highest one) is stored in the result. |
158 | local tiflip = function(t) |
159 | local r = { } |
160 | |
161 | for i = 1, #t do |
162 | r[t[i]] = i |
163 | end |
164 | |
165 | return r |
166 | end |
167 | |
168 | local tset = function(t) |
169 | local r = { } |
170 | |
171 | for k, v in pairs(t) do |
172 | r[v] = true |
173 | end |
174 | |
175 | return r |
176 | end |
177 | |
178 | local tiset = function(t) |
179 | local r = { } |
180 | |
181 | for i = 1, #t do |
182 | r[t[i]] = true |
183 | end |
184 | |
185 | return r |
186 | end |
187 | |
188 | local function tiinsert_args(t, a, ...) |
189 | if a ~= nil then |
190 | t[#t + 1] = a |
191 | -- Recursion is usually faster than calling select() in a loop. |
192 | return tiinsert_args(t, ...) |
193 | end |
194 | |
195 | return t |
196 | end |
197 | |
198 | local timap_inplace = function(fn, t, ...) |
199 | for i = 1, #t do |
200 | t[i] = fn(t[i], ...) |
201 | end |
202 | |
203 | return t |
204 | end |
205 | |
206 | local timap = function(fn, t, ...) |
207 | local r = { } |
208 | for i = 1, #t do |
209 | r[i] = fn(t[i], ...) |
210 | end |
211 | return r |
212 | end |
213 | |
214 | local timap_sliding = function(fn, t, ...) |
215 | local r = {} |
216 | |
217 | for i = 1, #t do |
218 | tiinsert_args(r, fn(t[i], ...)) |
219 | end |
220 | |
221 | return r |
222 | end |
223 | |
224 | local tiwalk = function(fn, t, ...) |
225 | for i = 1, #t do |
226 | fn(t[i], ...) |
227 | end |
228 | end |
229 | |
230 | local tiwalker = function(fn) |
231 | return function(t) |
232 | for i = 1, #t do |
233 | fn(t[i]) |
234 | end |
235 | end |
236 | end |
237 | |
238 | local twalk_pairs = function(fn, t) |
239 | for k, v in pairs(t) do |
240 | fn(k, v) |
241 | end |
242 | end |
243 | |
244 | local tequals = function(lhs, rhs) |
245 | for k, v in pairs(lhs) do |
246 | if v ~= rhs[k] then |
247 | return false |
248 | end |
249 | end |
250 | |
251 | for k, v in pairs(rhs) do |
252 | if lhs[k] == nil then |
253 | return false |
254 | end |
255 | end |
256 | |
257 | return true |
258 | end |
259 | |
260 | local tiunique = function(t) |
261 | return tkeys(tiflip(t)) |
262 | end |
263 | |
264 | -- Deprecated, use tgenerate_1d_linear instead |
265 | local tgenerate_n = function(n, generator, ...) |
266 | local r = { } |
267 | for i = 1, n do |
268 | r[i] = generator(...) |
269 | end |
270 | return r |
271 | end |
272 | |
273 | local tgenerate_1d_linear = function(n, fn, ...) |
274 | local r = { } |
275 | for i = 1, n do |
276 | r[#r + 1] = fn(i, ...) |
277 | end |
278 | return r |
279 | end |
280 | |
281 | local tgenerate_2d_linear = function(w, h, fn, ...) |
282 | local r = { } |
283 | for y = 1, h do |
284 | for x = 1, w do |
285 | r[#r + 1] = fn(x, y, ...) |
286 | end |
287 | end |
288 | return r |
289 | end |
290 | |
291 | local taccumulate = function(t, init) |
292 | local sum = init or 0 |
293 | for k, v in pairs(t) do |
294 | sum = sum + v |
295 | end |
296 | return sum |
297 | end |
298 | |
299 | local tnormalize, tnormalize_inplace |
300 | do |
301 | local impl = function(t, r, sum) |
302 | sum = sum or taccumulate(t) |
303 | |
304 | for k, v in pairs(t) do |
305 | r[k] = v / sum |
306 | end |
307 | |
308 | return r |
309 | end |
310 | |
311 | tnormalize = function(t, sum) |
312 | return impl(t, { }, sum) |
313 | end |
314 | |
315 | tnormalize_inplace = function(t, sum) |
316 | return impl(t, t, sum) |
317 | end |
318 | end |
319 | |
320 | local tclone |
321 | do |
322 | local function impl(t, visited) |
323 | local t_type = type(t) |
324 | if t_type ~= "table" then |
325 | return t |
326 | end |
327 | |
328 | assert(not visited[t], "recursion detected") |
329 | visited[t] = true |
330 | |
331 | local r = { } |
332 | for k, v in pairs(t) do |
333 | r[impl(k, visited)] = impl(v, visited) |
334 | end |
335 | |
336 | visited[t] = nil |
337 | |
338 | return r |
339 | end |
340 | |
341 | tclone = function(t) |
342 | return impl(t, { }) |
343 | end |
344 | end |
345 | |
346 | -- Slow |
347 | local tcount_elements = function(t) |
348 | local n = 0 |
349 | for _ in pairs(t) do |
350 | n = n + 1 |
351 | end |
352 | return n |
353 | end |
354 | |
355 | local tremap_to_array = function(fn, t) |
356 | local r = { } |
357 | for k, v in pairs(t) do |
358 | r[#r + 1] = fn(k, v) |
359 | end |
360 | return r |
361 | end |
362 | |
363 | local tmap_values = function(fn, t, ...) |
364 | local r = { } |
365 | for k, v in pairs(t) do |
366 | r[k] = fn(v, ...) |
367 | end |
368 | return r |
369 | end |
370 | |
371 | -------------------------------------------------------------------------------- |
372 | |
373 | local torderedset = function(t) |
374 | local r = { } |
375 | |
376 | for i = 1, #t do |
377 | local v = t[i] |
378 | |
379 | -- Have to add this limitation to avoid size ambiquity. |
380 | -- If you need ordered set of numbers, use separate storage |
381 | -- for set and array parts (write make_ordered_set then). |
382 | assert(type(v) ~= "number", "can't insert number into ordered set") |
383 | |
384 | r[v] = i |
385 | r[i] = v |
386 | end |
387 | |
388 | return r |
389 | end |
390 | |
391 | -- Returns false if item already exists |
392 | -- Returns true otherwise |
393 | local torderedset_insert = function(t, v) |
394 | -- See torderedset() for motivation |
395 | assert(type(v) ~= "number", "can't insert number into ordered set") |
396 | |
397 | if not t[v] then |
398 | local i = #t + 1 |
399 | t[v] = i |
400 | t[i] = v |
401 | |
402 | return true |
403 | end |
404 | |
405 | return false |
406 | end |
407 | |
408 | -- Returns false if item didn't existed |
409 | -- Returns true otherwise |
410 | -- Note this operation is really slow |
411 | local torderedset_remove = function(t, v) |
412 | -- See torderedset() for motivation |
413 | assert(type(v) ~= "number", "can't remove number from ordered set") |
414 | |
415 | local pos = t[v] |
416 | if pos then |
417 | t[v] = nil |
418 | -- TODO: Do table.remove manually then to do all in a single loop. |
419 | table_remove(t, pos) |
420 | for i = pos, #t do |
421 | t[t[i]] = i -- Update changed numbers |
422 | end |
423 | end |
424 | |
425 | return false |
426 | end |
427 | |
428 | -------------------------------------------------------------------------------- |
429 | |
430 | -- Handles subtables (is "deep"). |
431 | -- Does not support recursive defaults tables |
432 | -- WARNING: Uses tclone()! Do not use on tables with metatables! |
433 | local twithdefaults |
434 | do |
435 | twithdefaults = function(t, defaults) |
436 | for k, d in pairs(defaults) do |
437 | local v = t[k] |
438 | if v == nil then |
439 | if type(d) == "table" then |
440 | d = tclone(d) |
441 | end |
442 | t[k] = d |
443 | elseif type(v) == "table" and type(d) == "table" then |
444 | twithdefaults(v, d) |
445 | end |
446 | end |
447 | |
448 | return t |
449 | end |
450 | end |
451 | |
452 | -------------------------------------------------------------------------------- |
453 | |
454 | local tifilter = function(pred, t, ...) |
455 | local r = { } |
456 | for i = 1, #t do |
457 | local v = t[i] |
458 | if pred(v, ...) then |
459 | r[#r + 1] = v |
460 | end |
461 | end |
462 | return r |
463 | end |
464 | |
465 | -------------------------------------------------------------------------------- |
466 | |
467 | local tsetof = function(value, t) |
468 | local r = { } |
469 | |
470 | for k, v in pairs(t) do |
471 | r[v] = value |
472 | end |
473 | |
474 | return r |
475 | end |
476 | |
477 | -------------------------------------------------------------------------------- |
478 | |
479 | local tset_many = function(...) |
480 | local r = { } |
481 | |
482 | for i = 1, select("#", ...) do |
483 | for k, v in pairs((select(i, ...))) do |
484 | r[v] = true |
485 | end |
486 | end |
487 | |
488 | return r |
489 | end |
490 | |
491 | -- TODO: Pick a better name? |
492 | local tidentityset = function(t) |
493 | local r = { } |
494 | |
495 | for k, v in pairs(t) do |
496 | r[v] = v |
497 | end |
498 | |
499 | return r |
500 | end |
501 | |
502 | -------------------------------------------------------------------------------- |
503 | |
504 | local timapofrecords = function(t, key) |
505 | local r = { } |
506 | |
507 | for i = 1, #t do |
508 | local v = t[i] |
509 | r[assert(v[key], "missing record key field")] = v |
510 | end |
511 | |
512 | return r |
513 | end |
514 | |
515 | local tivalues = function(t) |
516 | local r = { } |
517 | |
518 | for i = 1, #t do |
519 | r[#r + 1] = t[i] |
520 | end |
521 | |
522 | return r |
523 | end |
524 | |
525 | -------------------------------------------------------------------------------- |
526 | |
527 | -- NOTE: Optimized to be fast at simple value indexing. |
528 | -- Slower on initialization and on table value fetching. |
529 | -- WARNING: This does not protect userdata. |
530 | local treadonly, treadonly_ex |
531 | do |
532 | local newindex = function() |
533 | error("attempted to change read-only table") |
534 | end |
535 | |
536 | treadonly = function(value, callbacks, tostring_fn, disable_nil) |
537 | callbacks = callbacks or empty_table |
538 | if disable_nil == nil then |
539 | disable_nil = true |
540 | end |
541 | |
542 | arguments( |
543 | "table", value, |
544 | "table", callbacks |
545 | ) |
546 | |
547 | optional_arguments( |
548 | "function", tostring_fn, |
549 | "boolean", disable_nil -- TODO: ?! Not exactly optional |
550 | ) |
551 | |
552 | local mt = |
553 | { |
554 | __metatable = "treadonly"; -- protect metatable |
555 | |
556 | __index = function(t, k) |
557 | local v = rawget(value, k) |
558 | if is_table(v) then |
559 | -- TODO: Optimize |
560 | v = treadonly(v, callbacks, tostring_fn, disable_nil) |
561 | end |
562 | if v == nil then -- TODO: Try to use metatables |
563 | -- Note: __index does not support multiple return values in 5.1, |
564 | -- so we can not do call right here. |
565 | local fn = callbacks[k] |
566 | if fn then |
567 | return function(...) return fn(value, ...) end |
568 | end |
569 | if disable_nil then |
570 | error( |
571 | "attempted to read inexistant value at key " .. tostring(k), |
572 | 2 |
573 | ) |
574 | end |
575 | end |
576 | return v |
577 | end; |
578 | |
579 | __newindex = newindex; |
580 | } |
581 | |
582 | if tostring_fn then |
583 | mt.__tostring = function() return tostring_fn(value) end |
584 | end |
585 | |
586 | return setmetatable({ }, mt) |
587 | end |
588 | |
589 | -- Changes to second return value are guaranteed to affect first one |
590 | treadonly_ex = function(value, ...) |
591 | local protected = treadonly(value, ...) |
592 | return protected, value |
593 | end |
594 | end |
595 | |
596 | local tmap_kv = function(fn, t) |
597 | local r = { } |
598 | for k, v in pairs(t) do |
599 | k, v = fn(k, v) |
600 | r[k] = v |
601 | end |
602 | return r |
603 | end |
604 | |
605 | local tmapofrecordgroups = function(t, key_name) |
606 | local r = { } |
607 | for k, v in pairs(t) do |
608 | local v = t[k] |
609 | local key = assert(v[key_name], "missing required key") |
610 | local g = r[key] |
611 | if not g then |
612 | g = { } |
613 | r[key] = g |
614 | end |
615 | g[#g + 1] = v |
616 | end |
617 | |
618 | return r |
619 | end |
620 | |
621 | local timapofrecordgroups = function(t, key_name) |
622 | local r = { } |
623 | for i = 1, #t do |
624 | local v = t[i] |
625 | local key = assert(v[key_name], "missing required key") |
626 | local g = r[key] |
627 | if not g then |
628 | g = { } |
629 | r[key] = g |
630 | end |
631 | g[#g + 1] = v |
632 | end |
633 | |
634 | return r |
635 | end |
636 | |
637 | local tilistofrecordfields = function(t, k) |
638 | local r = { } |
639 | for i = 1, #t do |
640 | local v = t[i][k] |
641 | assert(v ~= nil, "missing required key") |
642 | r[#r + 1] = v |
643 | end |
644 | return r |
645 | end |
646 | |
647 | local tipermute_inplace = function(t, n, count, random) |
648 | n = n or #t |
649 | count = count or n |
650 | random = random or math.random |
651 | |
652 | for i = 1, count do |
653 | local j = random(i, n) |
654 | t[i], t[j] = t[j], t[i] |
655 | end |
656 | |
657 | return t |
658 | end |
659 | |
660 | local tkvtorecordlist = function(t, key_name, value_name) |
661 | local result = { } |
662 | for k, v in pairs(t) do |
663 | result[#result + 1] = { [key_name] = k, [value_name] = v } |
664 | end |
665 | return result |
666 | end |
667 | |
668 | local function tgetpath(t, k, nextk, ...) |
669 | if k == nil then |
670 | return nil |
671 | end |
672 | |
673 | local v = t[k] |
674 | if not is_table(v) or nextk == nil then |
675 | return v |
676 | end |
677 | |
678 | return tgetpath(v, nextk, ...) |
679 | end |
680 | |
681 | -- tsetpath(tabl, "a", "b", "c", d) |
682 | -- tabl.a.b.c[d] = val |
683 | local tsetpath |
684 | do |
685 | local function impl(nargs, dest, key, ...) |
686 | |
687 | if nargs == 0 then |
688 | return dest |
689 | end |
690 | |
691 | if key == nil then |
692 | error("tsetpath: nil can't be a table key") |
693 | end |
694 | |
695 | dest[key] = assert_is_table( |
696 | dest[key] or { }, |
697 | "key `" .. tostring(key) |
698 | .. "' already exists and its value is not a table" |
699 | ) |
700 | |
701 | return impl(nargs - 1, dest[key], ...) |
702 | end |
703 | |
704 | tsetpath = function(dest, ...) |
705 | local nargs = select("#", ...) |
706 | if nargs == 0 then |
707 | return dest |
708 | end |
709 | |
710 | return impl(nargs, dest, ...) |
711 | end |
712 | end |
713 | |
714 | local tsetpathvalue |
715 | do |
716 | local function impl(nargs, value, dest, key, ...) |
717 | assert(nargs > 0) |
718 | |
719 | if key == nil then |
720 | error("tsetpathvalue: nil can't be a table key") |
721 | end |
722 | |
723 | if nargs == 1 then |
724 | dest[key] = value |
725 | return dest |
726 | end |
727 | |
728 | dest[key] = assert_is_table( |
729 | dest[key] or { }, |
730 | "key `" .. tostring(key) |
731 | .. "' already exists and its value is not a table" |
732 | ) |
733 | |
734 | return impl(nargs - 1, value, dest[key], ...) |
735 | end |
736 | |
737 | tsetpathvalue = function(value, dest, ...) |
738 | local nargs = select("#", ...) |
739 | if nargs == 0 then |
740 | return dest |
741 | end |
742 | |
743 | return impl(nargs, value, dest, ...) |
744 | end |
745 | end |
746 | |
747 | -- TODO: rename to tislice |
748 | local tslice = function(t, start_i, end_i) |
749 | local r = { } |
750 | |
751 | start_i = math_max(start_i, 1) |
752 | end_i = math_min(end_i, #t) |
753 | for i = start_i, end_i do |
754 | r[i - start_i + 1] = t[i] |
755 | end |
756 | |
757 | return r |
758 | end |
759 | |
760 | local tarraylisttohashlist = function(t, ...) |
761 | local r = { } |
762 | local nargs = select("#", ...) |
763 | |
764 | for i = 1, #t do |
765 | local item = { } |
766 | for j = 1, nargs do |
767 | local hash = select(j, ...) |
768 | if hash ~= nil then -- ignore nil from arguments |
769 | item[hash] = t[i][j] |
770 | end |
771 | end |
772 | r[#r + 1] = item |
773 | end |
774 | |
775 | return r |
776 | end |
777 | |
778 | local tarraytohash = function(t, ...) |
779 | local r = { } |
780 | local nargs = select("#", ...) |
781 | |
782 | for i = 1, nargs do |
783 | local hash = select(i, ...) |
784 | if hash ~= nil then -- ignore nil from arguments |
785 | r[hash] = t[i] |
786 | end |
787 | end |
788 | |
789 | return r |
790 | end |
791 | |
792 | local tisempty = function(t) |
793 | return next(t) == nil |
794 | end |
795 | |
796 | local tifindvalue_nonrecursive = function(t, v) |
797 | for i = 1, #t do |
798 | if t[i] == v then |
799 | return true |
800 | end |
801 | end |
802 | return false |
803 | end |
804 | |
805 | local tkvlist2kvpairs = function(t) |
806 | local r = { } |
807 | for i = 1, #t, 2 do |
808 | local k, v = t[i], t[i+1] |
809 | if k ~= nil then |
810 | r[k] = v |
811 | end |
812 | end |
813 | return r |
814 | end |
815 | |
816 | local tfilterkeylist = function(t, f, strict) |
817 | strict = strict or false |
818 | local r = { } |
819 | |
820 | for i = 1, #f do |
821 | local k = f[i] |
822 | if t[k] ~= nil then |
823 | r[k] = t[k] |
824 | elseif strict then |
825 | return nil, "Field `" .. tostring(k) .. "' is absent" |
826 | end |
827 | end |
828 | return r |
829 | end |
830 | |
831 | local tkvmap_unpack = function(fn, t, ...) |
832 | local r = { } |
833 | for k, v in pairs(t) do |
834 | k, v = fn(k, ...), fn(v, ...) |
835 | |
836 | if k ~= nil and v ~= nil then |
837 | r[#r + 1] = k |
838 | r[#r + 1] = v |
839 | end |
840 | end |
841 | return unpack(r) |
842 | end |
843 | |
844 | local tkvlist_to_hash = function(t) |
845 | local r = { } |
846 | for i = 1, #t, 2 do |
847 | r[t[i]] = t[i + 1] |
848 | end |
849 | return r |
850 | end |
851 | |
852 | local tmerge_many = function(...) |
853 | return toverride_many({ }, ...) |
854 | end |
855 | |
856 | -- Returns true is a table is an array |
857 | -- Returns false otherwise |
858 | -- Note the empty table is treated as an array |
859 | local tisarray = function(t) |
860 | for k, _ in pairs(t) do |
861 | if |
862 | -- Array keys should be numbers... |
863 | not is_number(k) |
864 | -- ...greater than 1... |
865 | or k < 1 |
866 | -- ...in a continuous sequence... |
867 | or (k > 1 and t[k - 1] == nil) |
868 | -- ...of integers... |
869 | or k % 1 ~= 0 |
870 | -- ...avoiding floating point overflow |
871 | or k == k - 1 |
872 | then |
873 | return false |
874 | end |
875 | end |
876 | return true |
877 | end |
878 | |
879 | -------------------------------------------------------------------------------- |
880 | |
881 | return |
882 | { |
883 | empty_table = empty_table; |
884 | toverride_many = toverride_many; |
885 | tappend_many = tappend_many; |
886 | tijoin_many = tijoin_many; |
887 | tkeys = tkeys; |
888 | tvalues = tvalues; |
889 | tkeysvalues = tkeysvalues; |
890 | tflip = tflip; |
891 | tflip_inplace = tflip_inplace; |
892 | tiflip = tiflip; |
893 | tset = tset; |
894 | tiset = tiset; |
895 | tisarray = tisarray; |
896 | tiinsert_args = tiinsert_args; |
897 | timap_inplace = timap_inplace; |
898 | timap = timap; |
899 | timap_sliding = timap_sliding; |
900 | tiwalk = tiwalk; |
901 | tiwalker = tiwalker; |
902 | tequals = tequals; |
903 | tiunique = tiunique; |
904 | tgenerate_n = tgenerate_n; -- deprecated |
905 | tgenerate_1d_linear = tgenerate_1d_linear; |
906 | tgenerate_2d_linear = tgenerate_2d_linear; |
907 | taccumulate = taccumulate; |
908 | tnormalize = tnormalize; |
909 | tnormalize_inplace = tnormalize_inplace; |
910 | tclone = tclone; |
911 | tcount_elements = tcount_elements; |
912 | tremap_to_array = tremap_to_array; |
913 | twalk_pairs = twalk_pairs; |
914 | tmap_values = tmap_values; |
915 | torderedset = torderedset; |
916 | torderedset_insert = torderedset_insert; |
917 | torderedset_remove = torderedset_remove; |
918 | twithdefaults = twithdefaults; |
919 | tifilter = tifilter; |
920 | tsetof = tsetof; |
921 | tset_many = tset_many; |
922 | tidentityset = tidentityset; |
923 | timapofrecords = timapofrecords; |
924 | tivalues = tivalues; |
925 | treadonly = treadonly; |
926 | treadonly_ex = treadonly_ex; |
927 | tmap_kv = tmap_kv; |
928 | tmapofrecordgroups = tmapofrecordgroups; |
929 | timapofrecordgroups = timapofrecordgroups; |
930 | tilistofrecordfields = tilistofrecordfields; |
931 | tipermute_inplace = tipermute_inplace; |
932 | tkvtorecordlist = tkvtorecordlist; |
933 | tgetpath = tgetpath; |
934 | tsetpath = tsetpath; |
935 | tsetpathvalue = tsetpathvalue; |
936 | tslice = tslice; |
937 | tarraylisttohashlist = tarraylisttohashlist; |
938 | tarraytohash = tarraytohash; |
939 | tkvlist2kvpairs = tkvlist2kvpairs; |
940 | tfilterkeylist = tfilterkeylist; |
941 | tisempty = tisempty; |
942 | tifindvalue_nonrecursive = tifindvalue_nonrecursive; |
943 | tkvmap_unpack = tkvmap_unpack; |
944 | tkvlist_to_hash = tkvlist_to_hash; |
945 | tmerge_many = tmerge_many; |
946 | } |
test run test run with input download show line numbers
Travelled to 12 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Recognizer | Recognition Result | Visualize | Recalc |
---|---|---|---|
#308 | 19795 | [visualize] |
Snippet ID: | #157 |
Snippet name: | table-utils.lua (luanucleo) |
Eternal ID of this version: | #157/1 |
Text MD5: | 886ad3506df943b2b2a59e5315f48d1a |
Author: | stefan |
Category: | |
Type: | Lua code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2014-01-13 03:45:08 |
Source code size: | 19795 bytes / 946 lines |
Pitched / IR pitched: | Yes / Yes |
Views / Downloads: | 1105 / 315 |
Referenced in: | [show references] |