Example:
mytable = {{id=100,wordform="One Hundread"},{id=200,wordform="Two Hundread"}}
I want to be able to access the row based on the id to do something like this:
mynum = 100
print ("The value is " .. mytable[mynum].wordform)
The main point here is I want to be able to set the index value so I can predictably retrieve the associated value later as I might do with a java hashmap.
Ideally your table should just use the ID as key:
local mytable = {[100] = {wordform = "One hundred"}, [200] = {wordform = "Two hundred"}}
print("The value is " .. mytable[100].wordform)
If your table is in list form, you can convert it to ID form rather easily:
local mytable_by_id = {}
for _, item in pairs(mytable) do mytable_by_id[item.id] = item end
Using the hash part of a Lua table is definitely preferable over looping over the list entries in linear time as Renshaw's answer suggests (the fact that it's hidden behind a metatable may hide the poor performance and trick the reader into believing a hash indexing operation is taking place here though).
Furthermore, that answer won't even work correctly, as the list part uses integer keys as well; IDs that are valid list part indices would possibly return the wrong element. You'd have to use a function instead of a metatable.
you can use metatable
setmetatable(mytable, {__index = function(tbl, id)
for _, item in pairs(tbl) do
if type(item) == "table" and item.id == id then
return item
end
end
end})
then
mynum = 100
print ("The value is " .. mytable[mynum].wordform) -- The value is One Hundread
Related
I have following lua code that prints out the Mac Addresses of a device.
local sc = Command.Scan.create()
local devices = sc:scan()
local topicMac
local list = {}
for _,device in pairs(devices) do
print(device:getMACAddress())
list[device] = device:getMACAddress()
end
topicMac = list[0]
print(topicMac)
Since there are several addresses and they are listed in a table, I would like to save only the first one into the local variable "topicMac". I tried reaching that first value by adding the first index (0 or 1) in the array.
Why do I get nil as return?
The next keyword can be used as a variant function to retrieve the first index and value out of a table
local index, value = next(tab) -- returns the first index and value of a table
so in your case:
local _, topicMac = next(list)
"First" and "Second" depends on what we have as keys. To check it just use print():
for k,d in pairs(devices) do
print(k,' = ',d:getMACAddress())
end
If keys are numbers, you can decide which is "first". If keys are strings, you are still able to make an algorithm to determine the first item in the table:
local the_first = "some_default_key"
for k,d in pairs(devices) do
if k < the_first then -- or use custom function: if isGreater(the_first,k) then
the_first = k
end
end
topicMac = devices[the_first]:getMACAddress()
print(topicMac)
If keys are objects or functions, you can't compare them directly. So you have to pick just any first item:
for _,d in pairs(devices) do
topicMac = d:getMACAddress()
break
end
print(topicMac)
What is the purpose of Line 2 In my code?
local table = {["First"] = 1, ["Second"] = 2, ["Third"] = 3}
for key, value in pairs(table) do
print(key)
end
Results-------------
First
Second
Third
What is the purpose of the line that says, "for key, value in pairs(table) do
Print(key) ?
I was wondering why it is essential.
As others have suggested in comments, you should really start by reading Programming in Lua. It will explain this and much more and is really a perfect place to start if you want to learn Lua.
Well then, as for what it does
Given a table like this
local tab = {first = 1, second = 2, third = 3}
the way you would usually iterate over all the key-value pairs in the table is like this
for key, value in pairs(tab) do
print(key .. ": " .. tostring(value))
end
This will loop over the three values in the table first = 1, second = 2, etc.
For each pair, key is set to the table key and value to its value. It then executes the code between do and end with those variables set.
So the example above will print the following:
first: 1
second: 2
third: 3
How does it work?
This is a bit more complex; Let's first of all see what pairs actually returns:
> t = {}
> print(pairs(t))
function: 68f18400 table: 0066b1d8 nil
The table it returns as its second argument is the same that we passed in.
The function that's returned by pairs is the next function, which, given a table and a key, returns the next key in the table, within an unknown order but without ever repeating keys.
You can easily confirm that on the command line.
> print(t)
table: 0066b1d8
> print(next)
function: 68f18400
Lua then turns the for loop into something like the following:
do
local f, state, iterator = next, tab, nil -- this is what's returned by pairs
while true do
local key, value = f(state, iterator)
if key == nil then break end
iterator = key
print(key, value) -- This is the body of our for loop
end
end
I have a problem,
I must change a value in a known table which is located in n tables before.
Something like this:
Box = {
{Name = "",Box = {{Name = "",Box = {{Name = "", Box = {{Name = "This must be change for the test",Box = {}}}}}}}}
}
To change this I can hardcode this with:
Box[1].Box[1].Box[1].Box[1].Name = "Changed"
But this isn't the way I want it!
I want to change this dynamically, so that I have one function which can change the value 'x' of table 'n' in the main table 'tbl'.
Is there any way to do this?
With the problem like that you probably should rethink your design choices. That table in n tables, you describe is akin to a tree graph where field box holds the references to children nodes.
As your comments state, you have the need of modifying a node located on a dynamic path in that tree. A simple function is obvious:
local function follow_path(start_node, path,i)
--takes start node and array of indices to follow
--returns table that is stored in the box-tree at this path
--i is for internal recursion
i=i or 1
if not path[i] then
return start_node
else
local new_start=start[path[i]]
assert(new_start,"No node is on this path")
return follow_path(new_start,path,i+1)
end
end
local function follow_path_and_rename(start_node,path,field_name,new_value)
local box = follow_path( start_node,path)
box[field_name] = new_value
end
But, depending on why do you need to modify a node with dynamic path there might be more solid approaches. One thing that never hurts is to give each of your boxes a unique identifier during its creation:
local new_box
do
local box_count=0
new_box = function(name)
box_count=box_count+1
local box = {
unique_id=box_count,
name=name,
box={}
}
return box
end
end
With that you could for example create index table where your boxes are always accessible by their id:
local index={}
local add_new_box = function(name)
local box = new_box(name)
index[ box.unique_id] = box
return box
end
Or if that is unacceptable, you could always search through the nodes of tree for a node that is guaranteed to have unique value.
But the thing is: all the tables do already have unique identifier. It is their address, the value that gets assigned to a when you do a = {}. The only differences between that, and the unique_id are:
1)a is not quite as readable.
2) if you know a, you don't need to search for it.
So, take a look at your problem, take a look and ask yourself:
"Where this need comes from? Why cannot I get unique_id instead a sequence of indices? Why cannot I get the box itself instead of unique_id?"
I'm inserting the value of an variable to a table and want to make sure the action succeeded.
Therefore I want to return the value, but not by the var, but from the table.
Is there a more simple way as iterating trough the table again?
Some way to remember the key of the value in the table, while it's inserted?
function(value)
for _,v in pairs(theTable) do
if v == value then
return --(due the table already contains the value)
end
end
table.insert(theTable, value)
return -- table.[VALUE]
end
local ix = #theTable + 1
theTable[ix] = value
That's pretty much what table.insert is doing:
As a special (and frequent) case, if we call insert without a position, it inserts the element in the last position of the array (and, therefore, moves no elements)
As a sidenote, your function is pretty inefficient; you're doing an O(n) "contains" check, which could be made much better if you created an index of values.
Is there a method for checking if a table contains a value ? I have my own (naive) function, but I was wondering if something "official" exists for that ? Or something more efficient...
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end
By the way, the main reason I'm using this functions is to use tables as sets, ie with no duplicate elements. Is there something else I could use ?
You can put the values as the table's keys. For example:
function addToSet(set, key)
set[key] = true
end
function removeFromSet(set, key)
set[key] = nil
end
function setContains(set, key)
return set[key] ~= nil
end
There's a more fully-featured example here.
Given your representation, your function is as efficient as can be done. Of course, as noted by others (and as practiced in languages older than Lua), the solution to your real problem is to change representation. When you have tables and you want sets, you turn tables into sets by using the set element as the key and true as the value. +1 to interjay.
I know this is an old post, but I wanted to add something for posterity.
The simple way of handling the issue that you have is to make another table, of value to key.
ie. you have 2 tables that have the same value, one pointing one direction, one pointing the other.
function addValue(key, value)
if (value == nil) then
removeKey(key)
return
end
_primaryTable[key] = value
_secodaryTable[value] = key
end
function removeKey(key)
local value = _primaryTable[key]
if (value == nil) then
return
end
_primaryTable[key] = nil
_secondaryTable[value] = nil
end
function getValue(key)
return _primaryTable[key]
end
function containsValue(value)
return _secondaryTable[value] ~= nil
end
You can then query the new table to see if it has the key 'element'. This prevents the need to iterate through every value of the other table.
If it turns out that you can't actually use the 'element' as a key, because it's not a string for example, then add a checksum or tostring on it for example, and then use that as the key.
Why do you want to do this? If your tables are very large, the amount of time to iterate through every element will be significant, preventing you from doing it very often. The additional memory overhead will be relatively small, as it will be storing 2 pointers to the same object, rather than 2 copies of the same object.
If your tables are very small, then it will matter much less, infact it may even be faster to iterate than to have another map lookup.
The wording of the question however strongly suggests that you have a large number of items to deal with.
I can't think of another way to compare values, but if you use the element of the set as the key, you can set the value to anything other than nil. Then you get fast lookups without having to search the entire table.
-- in some helper module
function utils_Set(list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end
-- your table here
long_table = { "v1", "v2", "v1000"}
-- Consult some value
_set = utils_Set(long_table)
if _set["v1"] then print("yes!") end