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?"
Related
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
I have a table which will be used to store each players name, id and another value
{
{
rpname = "name",
SteamID = "STEAM_0:0:",
giftsFound = "1",
},
The table is being sent from server to client via net.ReadTable()
I want to be able to choose each value seperatley but when I have tried the following below it only returns the first letter of every value instead of the first value
for k, v in pairs(tableData) do
for k, v in pairs(v) do
print(v[1]
end
end
Please could somebody help me out?
If I understood correctly, the sample table you wrote in the first block of code would be what you called tableData in the second, right? So, what you want to have is:
An array of players
Each entry in this array is a table
A way of getting each field of each player
With a few tweaks we can make your code more readable and, from that, correct it. Firsly, I would rename some things:
Rename your table players, for it is an array of players
local players = {
{
rpname = "john",
SteamID = "STEAM_0:0:1",
giftsFound = "4",
},
-- [...]
}
Rename your variables in the for-loop
In Lua it is common pratice to use _ to name variable we are not going to use. In this case, the key (originally named k) is not something we will use.
Since it is a list of players, each entry is a player, so it is logical to rename the variable v to player.
Also, I changed pairs() to ipairs() and there's a good reason for that. I won't cover it here, but here it is explained as best as I could. Rule of thumb: if your table is array-like, use ipairs(); else, use pairs().
for _, player in ipairs(players) do
-- [...]
end
For the nested for-loop, it does make sense using k, v and pairs, so it would be something like this:
for k, v in pairs(player) do
print(k,v)
end
Running the full piece would produce this:
rpname john
giftsFound 4
SteamID STEAM_0:0:1
I suppose it solves your problem. The real errors in your code were the way you tried to access the nested table field and, arguably, naming variables with names you have already used (k and v) in the same scope, which is, in the best case, misleading.
If you want to access a specific field in the table, instead of going through the whole thing, you can do:
-- To print every player's name
for _, player in ipairs(players) do
local name = player.rpname
print(name)
end
Or even:
-- To get the first player's (in the array) name
local name = players[1].rpname
One last thing: "Lua" is not an acronym, you don't need to use all capital letters. Lua was created in Brazil and here we speak portuguese. Lua means Moon in portuguese.
Brand new to lua as of a few hours ago, I have some moderate background in C++ and Java but nothing amazing.
I'm trying to work on an addon for a game that checks for players around me, and if so(within 10 yards) greets them.
It works great, except I ONLY want it to run once per player, as it would be spammy and annoying to constantly greet people.
I figured the best way to do this was to store their character name in an array, but I'm struggling to understand the syntax of arrays.
function Wave()
local totalObjects = GetObjectCount()
local shouldMessage = false
local player = GetActivePlayer()
arrayNames = {}
for i = 1, totalObjects do
local object = GetObjectWithIndex(i)
if object ~= player and UnitIsPlayer(object) == true and UnitIsDead(object) == false then
local yards = GetDistanceBetweenObjects(player, object)
local name = ObjectName(object)
----------------- The beginning of my issue ----------------
if yards < 10 and arrayNames[i] ~= name then -- if name isnt in array already?
arrayNames[i] = name -- trying to add the name to array
print(arrayNames[i])
break
end
end
end
if storeName then
end
end
The issue is that your table is getting cleared after each call to Wave. This is because you are doing arrayNames = {} inside your function, so each time it is run the table is set to a new empty table. you can define arrayNames outside of your Wave function or change it to arrayNames = arrayNames or {} The second option will set arrayNames equal to arrayNames when it is defined or to a new table if it is not defined.
Additionally
Your code only checks if the name exists in the array at the specific index, rather then checking the whole array. If the player's index can change then you will likely greet them again using this method.
You will need to go over the whole array too be sure you have not greeted this person already. This means as you greet more and more people the check will get longer and longer
Rather than use an array, I suggest using a set:
if yards < 10 and not arrayNames[name] then -- if name isnt in set already?
arrayNames[name] = true -- trying to add the name to set
print(name)
break
end
simply add to the table using the name as the key and setting the value to true this will provide O(1) performance for your check.
Here is more information on sets:
https://en.wikipedia.org/wiki/Set_(abstract_data_type)
In computer science, a set is an abstract data type that can store unique values, without any particular order. It is a computer implementation of the mathematical concept of a finite set. Unlike most other collection types, rather than retrieving a specific element from a set, one typically tests a value for membership in a set.
In lua, now I want to change field name of a table, like
in test1.lua
local t = {
player_id = 2,
item_id = 1,
}
return t
in test2.lua
local t = require "test1"
print( t.item_id )
if I want to change field name item_id -> item_count of t,
I need to use the application like ultraedit, find out all lua file contain item_id, and modified one by one, such modification easy correction or change of leakage, is there any tools can be more easily modified the field name?
If I understand your problem correctly:
(What if item_id also happens to be a field in a different table?)
In my view, there is no generic solution for this task without actually interpreting the script. A field name (e.g., item_id) could appear in many places but be referring to different tables. To make sure you change only references to the correct table you need to actually interpret the script. Given the dynamic nature of Lua scripts, this is not a trivial task.
Using an editor (or preferably a Lua script) to globally replace all occurrences of one name to another will only work if you're certain the 'old' name has only a single context.
A run-time workaround might be to add some metatable to keep both the new and the old name. I think you need to play with the __index and __newindex events. Then, the same field will be accessed by either 'old' or 'new' name.
Example for the metatable trick (only field 'one' is actually part of the table, field 'two' access is made possible via the metatable to read/write field 'one' so that reading from 'two' reads 'one', while writing to 'two' writes to 'one'):
t = {}
t.one = 1
setmetatable(t,{__index = function(t,k)
if k == 'two' then return t.one end
end,
__newindex = function(t,k,v)
if k == 'two' then t.one = v end
end
})
print(t.one,t.two)
t.two = 2
print(t.one,t.two)
First of all: I'm an inexperienced coder and just started reading PiL. I only know a thing or two but I'm fast learning and understanding. This method is really unnecessary but I sort of want to give myself a hard time in order to learn more.
Okay so for testing and for getting to know the language more, I'm trying to grab two different values from two different files and storing them in tables
local gamemap = file.Read("addons/easymap/data/maplist.txt", "GAME")
local mapname = string.Explode( ",", gamemap )
local mapid = file.Read("addons/easymap/data/mapid.txt", "GAME")
local id = string.Explode( ",", mapid )
I'm grabbing two values which in the end are mapname and id
Once I have them, I know that using
for k, v in pairs(mapname)
It will give specific values to the data taken from the file, or at least assign them.
But what I need to do with the both tables is that if there is certain map in the server, check for the value in the table unless the map name is nil and then once having the name, grab the value of that map and match it with the id of the other file.
For example, I have in the maplist.txt file gm_construct and it is the first entry [1] and its corresponding id in mapid.txt lets say it is 54321 and it is also the first entry [1].
But now I must check the server's current map with game.GetMap function, I have that solved and all, I grab the current map, match it with the mapname table and then check for its corresponding value in the id table, which would be gm_construct = 1.
For example it would be something like
local mapdl = game.GetMap()
local match = mapname[mapdl]
if( match != nil )then --supposing the match isn't nil and it is in the table
--grab its table value, lets say it is 1 and match it with the one in the id table
It is a more complex version of this http://pastebin.com/3652J8Pv
I know it is unnecessary but doing this script will give me more options to expand the script further.
TL;DR: I need to find a function that lets me match two values coming from different tables and files, but in the end they are in the same order ([1] = [1]) in both files. Or a way to fetch a full table from another file. I don't know if a table can be loaded globally and then grabbed by another file to use it in that file.
I'm sorry if I'm asking too much, but where I live, if you want to learn to program, you have to do it on your own, no schools have classes or anything similar, at least not until University, and I'm far away from even finishing High School.
Edit: this is intended to be used on Garry's mod. The string.Explode is explained here: http://wiki.garrysmod.com/page/string/Explode
It basically separates phrases by a designated character, in this case, a comma.
Okay. If I understand correctly... You have 2 Files with data.
One with Map Names
gm_construct,
gm_flatgrass,
de_dust2,
ttt_waterworld
And One with IDs, Numbers, Whataver (related to the entries at the same position in the Map Names File
1258,
8592,
1354,
2589
And now you want to find the ID of the current Map, right?
Here is your Function
local function GetCurrentMapID()
-- Get the current map
local cur_map = game.GetMap()
-- Read the Files and Split them
local mapListRaw = file.Read("addons/easymap/data/maplist.txt", "GAME")
local mapList= string.Explode(",", mapListRaw)
local mapIDsRaw = file.Read("addons/easymap/data/mapid.txt", "GAME")
local mapIDs = string.Explode(",", mapIDsRaw)
-- Iterate over the whole map list
for k, v in pairs(mapList) do
-- Until you find the current map
if (v == cur_map) then
-- then return the value from mapIDs which is located at the same key (k)
return mapIDs[k]
end
end
-- Throw a non-breaking error if the current map is not in the Maplist
ErrorNoHalt( "Current map is not registered in the Maplist!\n" )
end
Code could have errors 'cause I couldn't test it. Pls Comment with error if so.
Source: My Experience and the GMod Wiki