Iterate over table of tables - lua

I have this table local cookies = {{["name"]=23, ["value"]=333}, {["name"]=222, ["value"]=33233}} and I want to iterate over the subtables to find the one with the correct "name". Here is what I have tried
for _,elm in ipairs(cookies) do
for k,v in ipairs(elm) do
print(k)
if k == "name" and v == 222 then
print(v)
end
end
end
I does show in the outer for loop that it sees to tables, however, it does not even enter the inner for loop - why? How can I find the subtable for which "name" equals a certain value?

ipairs only iterates over the keys 1, 2, 3, ..., so it won't visit the key "name". If you want to visit all keys, use pairs (though be warned the order of iteration is not predictable).
However, for your example you don't need an inner loop at all. You can simple get the name of elm as elm.name:
for _,elm in ipairs(cookies) do
if elm.name == "222" then
print(elm.name, elm.value)
end
end
In fact, if you don't need the ordering or need to support duplicated cookie names, your cookies table could become a dictionary of name => value, allowing you to write this with no loops:
print(cookies["222"]) --> 33233

Related

LuA How to sort table from lowest value without key changes [duplicate]

I have a key => value table I'd like to sort in Lua. The keys are all integers, but aren't consecutive (and have meaning). Lua's only sort function appears to be table.sort, which treats tables as simple arrays, discarding the original keys and their association with particular items. Instead, I'd essentially like to be able to use PHP's asort() function.
What I have:
items = {
[1004] = "foo",
[1234] = "bar",
[3188] = "baz",
[7007] = "quux",
}
What I want after the sort operation:
items = {
[1234] = "bar",
[3188] = "baz",
[1004] = "foo",
[7007] = "quux",
}
Any ideas?
Edit: Based on answers, I'm going to assume that it's simply an odd quirk of the particular embedded Lua interpreter I'm working with, but in all of my tests, pairs() always returns table items in the order in which they were added to the table. (i.e. the two above declarations would iterate differently).
Unfortunately, because that isn't normal behavior, it looks like I can't get what I need; Lua doesn't have the necessary tools built-in (of course) and the embedded environment is too limited for me to work around it.
Still, thanks for your help, all!
You seem to misunderstand something. What you have here is a associative array. Associative arrays have no explicit order on them, e.g. it's only the internal representation (usually sorted) that orders them.
In short -- in Lua, both of the arrays you posted are the same.
What you would want instead, is such a representation:
items = {
{1004, "foo"},
{1234, "bar"},
{3188, "baz"},
{7007, "quux"},
}
While you can't get them by index now (they are indexed 1, 2, 3, 4, but you can create another index array), you can sort them using table.sort.
A sorting function would be then:
function compare(a,b)
return a[1] < b[1]
end
table.sort(items, compare)
As Komel said, you're dealing with associative arrays, which have no guaranteed ordering.
If you want key ordering based on its associated value while also preserving associative array functionality, you can do something like this:
function getKeysSortedByValue(tbl, sortFunction)
local keys = {}
for key in pairs(tbl) do
table.insert(keys, key)
end
table.sort(keys, function(a, b)
return sortFunction(tbl[a], tbl[b])
end)
return keys
end
items = {
[1004] = "foo",
[1234] = "bar",
[3188] = "baz",
[7007] = "quux",
}
local sortedKeys = getKeysSortedByValue(items, function(a, b) return a < b end)
sortedKeys is {1234,3188,1004,7007}, and you can access your data like so:
for _, key in ipairs(sortedKeys) do
print(key, items[key])
end
result:
1234 bar
3188 baz
1004 foo
7007 quux
hmm, missed the part about not being able to control the iteration. there
But in lua there is usually always a way.
http://lua-users.org/wiki/OrderedAssociativeTable
Thats a start. Now you would need to replace the pairs() that the library uses. That could be a simples as pairs=my_pairs. You could then use the solution in the link above
PHP arrays are different from Lua tables.
A PHP array may have an ordered list of key-value pairs.
A Lua table always contains an unordered set of key-value pairs.
A Lua table acts as an array when a programmer chooses to use integers 1, 2, 3, ... as keys. The language syntax and standard library functions, like table.sort offer special support for tables with consecutive-integer keys.
So, if you want to emulate a PHP array, you'll have to represent it using list of key-value pairs, which is really a table of tables, but it's more helpful to think of it as a list of key-value pairs. Pass a custom "less-than" function to table.sort and you'll be all set.
N.B. Lua allows you to mix consecutive-integer keys with any other kinds of keys in the same table—and the representation is efficient. I use this feature sometimes, usually to tag an array with a few pieces of metadata.
Coming to this a few months later, with the same query. The recommended answer seemed to pinpoint the gap between what was required and how this looks in LUA, but it didn't get me what I was after exactly :- which was a Hash sorted by Key.
The first three functions on this page DID however : http://lua-users.org/wiki/SortedIteration
I did a brief bit of Lua coding a couple of years ago but I'm no longer fluent in it.
When faced with a similar problem, I copied my array to another array with keys and values reversed, then used sort on the new array.
I wasn't aware of a possibility to sort the array using the method Kornel Kisielewicz recommends.
The proposed compare function works but only if the values in the first column are unique.
Here is a bit enhanced compare function to ensure, if the values of a actual column equals, it takes values from next column to evaluate...
With {1234, "baam"} < {1234, "bar"} to be true the items the array containing "baam" will be inserted before the array containing the "bar".
local items = {
{1004, "foo"},
{1234, "bar"},
{1234, "baam"},
{3188, "baz"},
{7007, "quux"},
}
local function compare(a, b)
for inx = 1, #a do
-- print("A " .. inx .. " " .. a[inx])
-- print("B " .. inx .. " " .. b[inx])
if a[inx] == b[inx] and a[inx + 1] < b[inx + 1] then
return true
elseif a[inx] ~= b[inx] and a[inx] < b[inx] == true then
return true
else
return false
end
end
return false
end
table.sort(items,compare)

table.insert/remove by value

I got two tables, for example:
table1 = { element1, element2, element3, element4 }
table2 = { element1, element3 }
Table 2 refers to some elements of table1, but I don't know which exactly, nor I know their index.
Now, for an specific element I want to check if table2 does contains it or not and insert/remove it in the case.
First thing that jumped to my mind was:
table.remove/insert(table2, table1.elementX)
But due insert/remove does its lookup by index, this doesn't work.
Sure, I could iterate through the whole table until I find the element and remove it, respectively until Iteration is done without match and insert it.
But is there a more performant method to do this?
I do not want to fill table2 with empty fields for bringing the elements on matching indices.
To insert, it's quite straightforward:
table.insert(table1, table2[index])
Unfortunately, to remove, it's a bit more tricky:
local ids = {} -- table containing ids to remove
for i,v in ipair(table1) do
if v == table2[index] then
table.insert(ids, 1, i) -- "1" preprends the value
end
-- At this point, "ids" contains all the ids to remove in the reverse order
for k,v in pair(ids) do
table.remove(table1, v)
end
What happens here is:
An intermediate table is created, it only contains the ids of the table to remove, descending. For example: { 6, 3, 1} (if the value is present 3 times).
That intermediate table is used to update the main table, as you can't use the ids from a table you are updating in a loop (that's what the comments about "transversal" mean).
Note that those operations must be made from the end of the table, because removing an element will change the ids of the following ones.
First revert table2 with
table2reverse = {}
for k,v in pairs(table2) do table2reverse[v]=k end
Then do this:
for k,v in pairs(table1)do
if table2reverse[v] then
table1[k]=nil
end
Finally compact table1.
for k,v in pairs(table1)do
if v == table2[index] then
table.remove/insert(table1, k)
break
end
Of course this works, but I still hope there's a more performante solution.
Due in case of multiple 1000 entrys in table1 and multiple 100 entrys in table2, this is will lead to high cpu usage, wich I want to avoid. (programming a controller with only 200mhz)

How to compare a bunch of values with themselves

I have a list of indexes I need my users to input, and I need the code to check if any of them are repeated, so it would give an error (they cannot be repeat).
if i only had two indexes it would be simple as :
if indexa == indexb then error() end
but its a fairly long list.
Here's a basic algorithm for detecting repeats.
-- This table is what's known as a set.
local indexes = {}
while true do
local index = getIndexFromUser()
-- Check for end of input.
if not index then
break
end
-- Check for repeats.
if indexes[index] then
error()
end
-- Store index as a key in indexes.
indexes[index] = true
end
In other words, table keys cannot be repeated, so you can simply store any non-nil value in a table under that key. Later (in future iterations of the loop), you can check to see whether that key is nil.
You could put all the indices in a table, use table.sort to sort them, then loop over table items to test if any consecutive items are identical:
indices = {1,6,3,0,3,5} -- will raise error
indices = {1,6,3,0,4,5} -- will not raise error
table.sort(indices)
for i=1, (#indices-1) do
if indices[i] == indices[i+1] then
error('not allowed duplicates')
end
end

Assign table to table in Lua

In Lua, I can add an entry inside table with table.insert(tableName, XYZ). Is there a way I can add already existing table into table? I mean a directly call rather then traversing and add it.
Thanks
The insert in your example will work fine with whatever contents happen to be inside the XYZ variable (number, string, table, function, etc.).
That will not copy the table though it will insert the actual table. If you want to insert a copy of the table then you need to traverse it and insert the contents.
First: In general, you do not need table.insert to put new entries into tables.
A table in Lua is a collection of key-value pairs; entries can be made like this:
local t = {} --the table
local key= "name"
local value = "Charlie"
t[key] = value --make a new entry (replace an existing value at the same key!)
print(t.name) --> "Charlie"
Note that key can have any type (not just integer/string)!
Very often you will need tables for a simple special case of this: A sequence ("list", "array") of values. For Lua, this means you want a table where all the keys are consecutive integers, and contain all non-nil values. The table.insert function is intended for that special case: It allows you to insert a value at a certain position (or to append it at the end of the sequence if no position is specified):
local t = {"a", "b", "d"} --a sequence containing three strings (t[1] = "a", ...)
table.insert(t, "e") --append "e" to the sequence
table.insert(t, 3, "c") --insert "c" at index 3 (moving values at higher indices)
--print the whole sequence
for i=1,#t do
print(t[i])
end
If I understand what you mean correctly, you want to do this:
local t1 = {1, 2, 3}
local t2 = {4, 5, 6}
some_function(t1, t2)
-- t1 is now {1, 2, 3, 4, 5, 6}
There is indeed no way to do this without iterating t2. Here is a way to write some_function:
local some_function = function(t1, t2)
local n = #t1
for i=1,#t2 do t1[n+i] = t2[i] end
end
No, you must copy the second table's key/value pairs into the first table. Copying the existing values from the second table is what's known as a "shallow copy." The first table will reference the same objects as the second table.
This works under limited circumstances:
local n = #t1
for i=1,#t2 do t1[n+i] = t2[i] end
It does attempt to shift the t2 elements to just beyond the existing t1 elements. That could be a vital requirement but wasn't stated in the question.
It has a few of problems, though:
By using #t1 and #t2, it misses keys that aren't positive integers and can miss keys that are integers greater than a skipped integer key (i.e. never assigned or assigned nil).
It accesses the first table with an indexer so could invoke the __newindex metamethod. That probably wouldn't be desirable when only copying is wanted.
It accesses the second table with an indexer so could invoke the __index metamethod. That wouldn't be desirable when only copying is wanted.
You might think that ipairs could be used if only positive integer keys are wanted but it quits on the first nil value found so could miss even more than #t2 does.
Use pairs instead:
for key, value in pairs(t2) do
rawset( t1, key, value )
end
If you do want to avoid replacing existing t1 values when the keys match or otherwise map t2 keys in some way then that has to be defined in the requirements.
Bottom line: pairs is the way to get all the keys. It effectively does a rawget so it avoids invoking __index. rawset is the way to do a copy without invoking __newindex.

Getting table entry index

I can't get table entry index. I need it to remove an item from table.
I use table.insert to add entries to table.
Another question: why Lua doesn't have "overload" to function table.remove so one can remove item by associative index?
Tables implement an unordered one to many relation between keys and values. In other words, any particular key (index) can only appear once in a table, but a value can appear multiple times.
If you know the key k, then t[k] = nil will remove both the key and the associated value from the table. However, this operation has no effect on any other keys or values in the table.
The table.insert and table.remove functions operate over the set of sequential integer keys beginning at 1, that are used by convention to implement arrays or lists. For that purpose, they manipulate other values in the list so as to keep the list from developing holes.
One way to find a key at which some value is found is to simply search the table. If this will be done more than once, then it is probably a good idea to build a second table that inverts the key/value pairs so that lookup by value is as fast as lookup by index.
A suitable implementation will depend on your assumptions and needs. Some samples are:
-- return the first integer index holding the value
function AnIndexOf(t,val)
for k,v in ipairs(t) do
if v == val then return k end
end
end
-- return any key holding the value
function AKeyOf(t,val)
for k,v in pairs(t) do
if v == val then return k end
end
end
-- return all keys holding the value
function AllKeysOf(t,val)
local s={}
for k,v in pairs(t) do
if v == val then s[#s+1] = k end
end
return s
end
-- invert a table so that each value is the key holding one key to that value
-- in the original table.
function Invert(t)
local i={}
for k,v in pairs(t) do
i[v] = k
end
return i
end
t[k]=nil removes from t the entry with key k.
For the second question, the answer is that tables can have individual metatables.

Resources