Assume the following array:
a = {"a", "b", "c"}
Using a[3], I can access c. But how do I make the string "repeat forever" (while it still only has elements)? Example:
a[4] --will return nil, but I need to to return "a", because 4 is 3 (end of array) + 1 (the element I need).
Question:
How would I make a[4] return the same value as a[1], if a[] consists of 3 elements?
You can either make sure that the key you use is within proper range or you move that logic into a's metatable by implementing the __index metamethod. That way you tell Lua what to return when someone accesses an invalid key in your table.
See http://lua-users.org/wiki/MetamethodsTutorial
local function circle(arr)
setmetatable(arr, {__index =
function(t, k)
if type(k) == "number" and #t > 0 then
return rawget(t, (k-1) % #t + 1)
end
end
})
end
local a = {"a", "b", "c"}
circle(a)
for j = -10, 10 do
print(j, a[j])
end
This is just a slight variation of Egor Skriptunoff's correct answer because I didn't want the formatting to be lost if left as comment.
In my view, this one makes the table creation a simpler single statement, and clearer as to the intended action. It also uses the same table as meta-table as a minor optimization.
local
function circle(arr)
function arr.__index(t, k)
if type(k) == 'number' and #t > 0 then
return rawget(t, (k-1) % #t + 1)
end
end
return setmetatable(arr,arr)
end
local a = circle {'a', 'b', 'c'}
for j = -10, 10 do print(j, a[j]) end
Related
I have two arrays:
A={4,8,12}
B={1,2,3,5,6,7,9,10,11}
And these code, the essence of which is, when calling the function, take a random value from array B, insert it in the order of the queue into array A, and, in another loop, remove that random value from array B.
local function Change ()
local q= math.random(1, #B)
for i = 1, #A do
if B[q]<A[i] then
for t=#A, i, -1 do
A[t+1]=A[t]
end
A[i]=B[q]
break
end
end
while q<= #B-1 do
B[q]=B[q+1]
q=q+1
end
B[q]=nil
for x = 1, #A do print ("Current: ", A[x]) end
for y = 1, #B do print ("Free: ", B[y]) end
end
Now I want to use the same code, for the inverse problem - take a random value from array A, insert it in the order of the queue into array B, and then delete the value from array A.
So I want something like this:
Change (A, B) – find random value in B and insert to A (arrays)
Change (B, A) – find random value in A and insert to B (arrays)
Is there any way in Lua to do this? Pointers or so...
P.S. I dont want to use "table.sort/table.insert", etc for this task.
P.P.S. We found solution in thread with pointer, but today I found another solution:
C={{4,8,12},{1,2,3,5,6,7,9,10,11}}
Local function Change (x,y)
a= math.random(1, #C[y])
for i = 1, #C[x] do
if C[y][a]<C[x][i] then
for t=#C[x], i, -1 do
C[x][t+1]=C[x][t]
end
C[x][i]=C[y][a]
break
end
end
while a<= #C[y]-1 do
C[y][a]=C[y][a+1]
a=a+1
end
C[y][a]=nil
for i = 1, #C[x] do print ("in C"..x, C[x][i]) end
for i = 1, #C[y] do print ("in C"..y, C[y][i]) end
end
and call Change(1,2) or Change (2,1)
How do you think, which method is better in performance?
Also I have another question in LUA syntax
When I make
C={ A={4,8,12}, B={1,2,3,5,6,7,9,10,11} }
And try inside the function #C[1] or #C[2] I got error «attempt to get length of field '?' (a nil value)» but it was very clear to write C.A or C.B instead of C[1], C[2] later in code
I had to initialize them like
A={4,8,12}, B={1,2,3,5,6,7,9,10,11}, C={A,B}
but got other error «attempt to get length of field ‘C’ (a nil value)» when use C.A or C.B.
In Lua, tables are the only objects which are shared when placed in variables.
mytable = {1,2,3}
newtable = mytable
newtable[1] = "one"
print(mytable[1]) --> prints: "one"
So in your code, we can change the hardcoded tables to variables:
local function Change(insertTable, valueTable)
local q = math.random(1, #valueTable)
for i = 1, #insertTable do
if valueTable[q] < insertTable[i] then
for t = #insertTable, i, -1 do
insertTable[t+1] = insertTable[t]
end
insertTable[i] = valueTable[q]
break
end
end
while q <= #valueTable-1 do
valueTable[q] = valueTable[q+1]
q = q+1
end
valueTable[q] = nil
for x = 1, #insertTable do print("Current: ", insertTable[x]) end
for y = 1, #valueTable do print("Free: ", valueTable[y]) end
end
How can I move the array bigint value from one index to another? For example, I have an array ARRAY[1, 2, 3, 4] of bigint unique values and want to move value 1 to index 3 so the final result would be ARRAY[2, 3, 1, 4]
The assumptions:
Element in the array identified by the value.
The uniqueness of the elements guaranteed.
Any element can be moved to any place.
Null values not involved on any side.
The value is contained in the array if not we have 2 options. First is do nothing and second handling of this exception by exception mechanism. It's an extreme case that can happen only because of some BUG
Arrays are 1-dimensional.
General assumptions:
Array elements are UNIQUE NOT NULL.
Arrays are 1-dimensional with standard subscripts (1..N). See:
Normalize array subscripts for 1-dimensional array so they start with 1
Simple solution
CREATE FUNCTION f_array_move_element_simple(_arr bigint[], _elem bigint, _pos int)
RETURNS bigint[] LANGUAGE sql IMMUTABLE AS
'SELECT a1[:_pos-1] || _elem || a1[_pos:] FROM array_remove(_arr, _elem) a1'
All fine & dandy, as long as:
The given element is actually contained in the array.
The given position is between 1 and the length of the array.
Proper solution
CREATE FUNCTION f_array_move_element(_arr ANYARRAY, _elem ANYELEMENT, _pos int)
RETURNS ANYARRAY AS
$func$
BEGIN
IF _pos IS NULL OR _pos < 1 THEN
RAISE EXCEPTION 'Target position % not allowed. Must be a positive integer.', _pos;
ELSIF _pos > array_length(_arr, 1) THEN
RAISE EXCEPTION 'Target position % not allowed. Cannot be greater than length of array.', _pos;
END IF;
CASE array_position(_arr, _elem) = _pos -- already in position, return org
WHEN true THEN
RETURN _arr;
WHEN false THEN -- remove element
_arr := array_remove(_arr, _elem);
ELSE -- element not found
RAISE EXCEPTION 'Element % not contained in array %!', _elem, _arr;
END CASE;
RETURN _arr[:_pos-1] || _elem || _arr[_pos:];
END
$func$ LANGUAGE plpgsql IMMUTABLE;
Exceptions are raised if any of the additional assumptions for the simple func are violated.
The "proper" function uses polymorphic types and works for any data type, not just bigint - as long as array and element type match.
db<>fiddle here
Postgresql supports slicing and appending so:
SELECT c, c[2:3] || c[1] || c[4:] AS result
FROM (SELECT ARRAY[1, 2, 3, 4] c) sub
db<>fiddle demo
Another variant using 'WITH .. SELECT ..' avoid searching elements by value, just array element numbers. jsonb[] with big jsons for example.
test_model.data - field to update.
:idx_from, :idx_to - placeholders, 1-based.
WITH from_removed AS (
SELECT
test_model.id,
ARRAY_CAT(
data[: :idx_from - 1],
data[:idx_from + 1 :]
) AS "d"
FROM test_model
)
UPDATE test_model AS source
SET data =
from_removed.d[: :idx_to - 1] ||
data[:idx_from : :idx_from] ||
from_removed.d[:idx_to :]
FROM from_removed
WHERE source.id = from_removed.id AND source.id = :id
Is there any way to loop trough a table like the one below in the same order as it's written?
local tbl = {
["hello"] = 1,
[2] = 2,
[50] = 3,
["bye"] = 4,
[200] = 5
}
What I mean is that when I use "in pairs" I'll get a different order everytime I execute my code ...
I'm searching for something like this:
function get_keys(tbl)
local rtable = {}
for k,v in pairs(tbl) do
table.insert(rtable, k)
end
return rtable
end
local keys_of_tbl = get_keys(tbl)
for i = 1, table.getn(keys_of_tbl) do
--Do something with: tbl[keys_of_tbl[i]]
end
But because the function "get_keys" is based on "in pairs" again, it won't work ...
In Lua, the order that pairs iterates through the keys is unspecified. However you can save the order in which items are added in an array-style table and use ipairs (which has a defined order for iterating keys in an array). To help with that you can create your own ordered table using metatables so that the key order will be maintained when new keys are added.
EDIT (earlier code inserted multiple copies of the key on updates)
To do this you can use __newindex which we be called so long as the index is not added yet to the table. The ordered_add method updates, deletes, or stores the key in the hidden tables _keys and _values. Note that __newindex will always be called when we update the key too since we didn't store the value in the table but instead stored it in the "hidden" tables _keys and _values.
Note however that we cannot use any key in this table, the key name "_keys" will overwrite our hidden table so the safer alternative is to use the ordered_table.insert(t, key, value) ordered_table.index(t, key) and ordered_table.remove(t, key) methods.
ordered_table = {}
function ordered_table.insert(t, k, v)
if not rawget(t._values, k) then -- new key
t._keys[#t._keys + 1] = k
end
if v == nil then -- delete key too.
ordered_table.remove(t, k)
else -- update/store value
t._values[k] = v
end
end
local function find(t, value)
for i,v in ipairs(t) do
if v == value then
return i
end
end
end
function ordered_table.remove(t, k)
local v = t._values[k]
if v ~= nil then
table.remove(t._keys, find(t._keys, k))
t._values[k] = nil
end
return v
end
function ordered_table.index(t, k)
return rawget(t._values, k)
end
function ordered_table.pairs(t)
local i = 0
return function()
i = i + 1
local key = t._keys[i]
if key ~= nil then
return key, t._values[key]
end
end
end
function ordered_table.new(init)
init = init or {}
local t = {_keys={}, _values={}}
local n = #init
if n % 2 ~= 0 then
error"in ordered_table initialization: key is missing value"
end
for i=1,n/2 do
local k = init[i * 2 - 1]
local v = init[i * 2]
if t._values[k] ~= nil then
error("duplicate key:"..k)
end
t._keys[#t._keys + 1] = k
t._values[k] = v
end
return setmetatable(t,
{__newindex=ordered_table.insert,
__len=function(t) return #t._keys end,
__pairs=ordered_table.pairs,
__index=t._values
})
end
--- Example Usage:
local t = ordered_table.new{
"hello", 1, -- key, value pairs
2, 2,
50, 3,
"bye", 4,
200, 5
}
print(#t)
print("hello is", t.hello)
print()
for k, v in pairs(t) do --- Lua 5.2 __pairs metamethod
print(k, v)
end
t.bye = nil -- delete that
t[2] = 7 -- use integer keys
print(#t)
No. There's no "as written in the source" order to tables. (Consider that not all keys necessarily exist in the source.) lua has no concept of "in order" for non-contiguous integer keys.
If you want a specific order you get to keep that order yourself manually in some way.
If you don't have any integer keys in your table then you can use those as your order (and use ipairs to loop those keys and index the value as the key to get the real value).
If your original values are the order you want to sort in then you can loop and reverse map to get a table that you can iterate with ipairs once done.
How would I modify (add/remove elements) an array while iterating over it and have the iterator be aware of it?
For example I would think this code:
a = "1234567890".split("")
a.each_with_index{|d, i|
if d.eql?('5')
a.delete_at(i)
a.insert(i, ['A', 'B', 'C'] )
end
print d
}
would produce: 1234ABC67890 but instead produces 1234567890
Is there a workaround or different method to make this work?
(I know this example is pretty simple example but I am doing some complicated text processing where I need to insert some text when I hit a key word. I do a bunch of functions before and after I would do the expansion so doing the insert outside of the each loop [aka map!] would really complicate my code)
Actually, your code works, you just need to replace print d with print a[i] since what you're printing is the variable d not the actual array element at index i
Rather than deleting and inserting, why not change the element on that index?
a = "1234567890".split("")
a.each_with_index{|d, i|
if d.eql?('5')
a[i] = ['A','B','C']
end
print a[i] #a[i] rather than d, since d contains the old value
}
or
...
if d.eql?('5')
a[i] = ['A','B','C']
d = a[i]
end
print d
Deleting/Inserting on an Array during iterations is discourage since it may cause headaches haha... Resort to other methods if possible :)
Note:
I've just used the current logic in your code based on my understanding, and the given desired output
the array will become [1,2,3,4,['A','B','C'],6,7,8,9,0] and not [1,2,3,4,'A','B','C',6,7,8,9,0]. If you want the other, just leave a comment :)
If what you want is just to change a value in the string, you can just use .tr or .gsub to do the job
Here is one option. If you want to return array then remove join otherwise keep it to return a String
a = "1234567890".split("")
a.collect! {|i| i == '5' ? ['A', 'B', 'C'] : i }.flatten!.join #=> "1234ABC67890"
Inserting and deleting while iterating is best avoided. Some problems disappear however when iterating in reverse order:
a = "1234567890".split("")
a.reverse_each.with_index{|d, i|
if d.eql?('5')
a.delete_at(i)
a.insert(i, ['A', 'B', 'C'] )
end
}
puts a.join # => 12345ABC7890
You can't in general modify an Enumerable while iterating over its members. In most such cases, you need to construct a new Enumerable as you go.
b = []
a.each_with_index do |d, i|
if d.eql?('5')
b << 'A' << 'B' << 'C'
else
b << d
end
end
I can't wrap my head around why record[x] would match up it's key name to a string in an array, then take it's value as the item to index the string by.. Is this some special feature of table.sort?
list = {"b", "c", "a"}
record = {a = 1, b = 2, c = 3}
table.sort(list, function (x, y) return record[x] < record[y] end)
for _, v in ipairs(list) do print(v) end
>a
>b
>c
The statement record = {a = 1, b = 2, c = 3} is equivalent to
record = {}
record["a"] = 1
record["b"] = 2
record["c"] = 3
This should make it clear how the values in list map to keys in record.
(I'll factor out the comparison function to make the explanation easier.)
list = {"b", "c", "a"}
record = {a = 1, b = 2, c = 3}
local function compare(x, y)
return record[x] < record[y]
end
table.sort(list, compare)
In the function compare, x and y may be any two elements of list. table.sort must call this function many times to figure out which elements are considered less than others. Without compare, table.sort would just use the < operator. As you can see in the code, compare refers to the record table when deciding whether to return true or false. table.sort merely calls compare without knowing anything about record.