Scripting Moderators ds1-e Posted March 28, 2019 Scripting Moderators Share Posted March 28, 2019 Hey, i'm creating temporary data system for player, with tables. What's the good and efficient way to check if table contains something. For example in table like that: playerTable = { playerItems = {} } table.insert(playerTable.playerItems, {"Item", 1}) for k, v in pairs(playerTable.playerItems) do outputChatBox("item: "..v[1]..", count: "..v[2]) -- item: Item, count: 1 end How i could check if table contains an item called "Item" and count of it? Link to comment
Moderators IIYAMA Posted March 28, 2019 Moderators Share Posted March 28, 2019 With a loop, as you are already doing. Or use a meta table on top of your table, which can be used as `firewall` + to keep track of inserted item counts. If you need an example for that, feel free to ask. Meta tables can be a bit confusing and I most of the time try not to use them as they can executing a lot of functions for just simple operations. 1 Link to comment
Scripting Moderators ds1-e Posted March 28, 2019 Author Scripting Moderators Share Posted March 28, 2019 7 hours ago, IIYAMA said: With a loop, as you are already doing. Or use a meta table on top of your table, which can be used as `firewall` + to keep track of inserted item counts. If you need an example for that, feel free to ask. Meta tables can be a bit confusing and I most of the time try not to use them as they can executing a lot of functions for just simple operations. But this is good way to do it, or it's a better soluton for it? 1 Link to comment
Moderators IIYAMA Posted March 28, 2019 Moderators Share Posted March 28, 2019 16 minutes ago, majqq said: But this is good way to do it, or it's a better soluton for it? Tables that are 'saved' inside of other tables do not become a different tables. So no, there are no better solutions, except if we optimise it for the code around it that is going to use it. This matters: - The frequently + when which operation of the table is used. If we ignore the types of methods to accomplish our goal, and only look at which options there are: A: Check every items and find out the information we need. B: Keep a record of which types of items we are adding and removing. Which one is better? That depends on the frequency which part of the code is used. There are two parts: 1. Modifications of the table. 2. Reading the table.(item count) (More table modification, than reading the table, pick option: A) (More reading the table, than table modification, pick option: B) 1 Link to comment
Moderators IIYAMA Posted March 29, 2019 Moderators Share Posted March 29, 2019 I noticed that I haven't gave you an useful examples. @majqq Well here you have an example of: B In OOP. Not MTA OOP, which I do not like, but pure Lua OOP. And see how the magic does it's job. Spoiler enchantedTable = { -- A simple `create` method to attach a collection of methods to your new table. create = function (self) local newTable = { collection = {}, itemCount = {} } local methods = self.methods for i=1, #methods do local method = methods[i] newTable[method.type] = method.func end return newTable end, -- These are all the methods you can attach to your enchanted table. methods = { { -- count all items or by type type = "count", func = function (self, itemType) if itemType == nil then return #self.collection else return self.itemCount[itemType] or 0 end end }, { -- get all items type = "get", func = function (self) return self.collection end }, { -- add an item type = "add", func = function (self, ...) local arguments = {...} for i=1, #arguments do local data = arguments[i] local collection = self.collection collection[#collection + 1] = data if type(data) == "table" and data.type ~= nil then local itemCount = self.itemCount itemCount[data.type] = (tonumber(itemCount[data.type]) or 0) + 1 end end return true end }, { -- remove an item type = "remove", func = function (self, index) if index ~= nil then local collection = self.collection local data = collection[index] if data ~= nil then table.remove(collection, index) if type(data) == "table" and data.type ~= nil then local itemCount = self.itemCount local count = itemCount[data.type] if count then count = count - 1 if count > 0 then itemCount[data.type] = count else itemCount[data.type] = nil end end end end return true end return false end } } } local newTable = enchantedTable:create() newTable:add({type = "yes", 1, 2, 3, 4}, {type = "no", 5, 6, 7, 8}, {type = "no", 5, 6, 7, 8}) -- add three items local collection = newTable:get() -- get the whole collection print(collection[1]) -- get the first print(collection[2]) -- get the second print(collection[3]) -- get the third -- Getting the result of the item records. print("all: ", newTable:count()) -- count all: 3 print("`yes`: ", newTable:count("yes")) -- count only type `yes`: 1 print("`no`: ", newTable:count("no")) -- count only type `no`: 2 newTable:remove(2) -- remove item 2. print("`no`: ", newTable:count("no")) -- count only type `no`: 1 When using this code you can easy extend functionality a long the way. Just adding methods to the methods-table and new functionality opens up to you. But remember, do not modify the collection (of the items) without using these functions, or the registration will not be able to get track of the item count. 2 Link to comment
Scripting Moderators ds1-e Posted April 10, 2019 Author Scripting Moderators Share Posted April 10, 2019 On 29/03/2019 at 20:28, IIYAMA said: I noticed that I haven't gave you an useful examples. @majqq Well here you have an example of: B In OOP. Not MTA OOP, which I do not like, but pure Lua OOP. And see how the magic does it's job. Reveal hidden contents enchantedTable = { -- A simple `create` method to attach a collection of methods to your new table. create = function (self) local newTable = { collection = {}, itemCount = {} } local methods = self.methods for i=1, #methods do local method = methods[i] newTable[method.type] = method.func end return newTable end, -- These are all the methods you can attach to your enchanted table. methods = { { -- count all items or by type type = "count", func = function (self, itemType) if itemType == nil then return #self.collection else return self.itemCount[itemType] or 0 end end }, { -- get all items type = "get", func = function (self) return self.collection end }, { -- add an item type = "add", func = function (self, ...) local arguments = {...} for i=1, #arguments do local data = arguments[i] local collection = self.collection collection[#collection + 1] = data if type(data) == "table" and data.type ~= nil then local itemCount = self.itemCount itemCount[data.type] = (tonumber(itemCount[data.type]) or 0) + 1 end end return true end }, { -- remove an item type = "remove", func = function (self, index) if index ~= nil then local collection = self.collection local data = collection[index] if data ~= nil then table.remove(collection, index) if type(data) == "table" and data.type ~= nil then local itemCount = self.itemCount local count = itemCount[data.type] if count then count = count - 1 if count > 0 then itemCount[data.type] = count else itemCount[data.type] = nil end end end end return true end return false end } } } local newTable = enchantedTable:create() newTable:add({type = "yes", 1, 2, 3, 4}, {type = "no", 5, 6, 7, 8}, {type = "no", 5, 6, 7, 8}) -- add three items local collection = newTable:get() -- get the whole collection print(collection[1]) -- get the first print(collection[2]) -- get the second print(collection[3]) -- get the third -- Getting the result of the item records. print("all: ", newTable:count()) -- count all: 3 print("`yes`: ", newTable:count("yes")) -- count only type `yes`: 1 print("`no`: ", newTable:count("no")) -- count only type `no`: 2 newTable:remove(2) -- remove item 2. print("`no`: ", newTable:count("no")) -- count only type `no`: 1 When using this code you can easy extend functionality a long the way. Just adding methods to the methods-table and new functionality opens up to you. But remember, do not modify the collection (of the items) without using these functions, or the registration will not be able to get track of the item count. Hey, probably i'm a bit stuck with it. So as I mentioned before, in other topic related to tables, i decided to give up with elementData's and create temporary data system within tables + sqlite/mysql. (tables for keep data in-game, and sqlite/mysql to save it after player disconnect). Everything goes well until now. I thought about creating something equivalent to getElementData, which will return false or item and itemcount. In example code: -- part of code cut from gui inv local row = guiGridListAddRow(gridlistItems["inventory"]) guiGridListSetItemText(gridlistItems["inventory"], row, gridlistItems["inventory_colum"], "Secondary Weapon", true, false) for i, item in ipairs(inventoryItems["Secondary Weapon"]) do if getElementData(getLocalPlayer(), item[1]) and getElementData(getLocalPlayer(), item[1]) >= 1 then local row = guiGridListAddRow(gridlistItems["inventory"]) guiGridListSetItemText(gridlistItems["inventory"], row, gridlistItems["inventory_colum"], item[1], false, false) guiGridListSetItemText(gridlistItems["inventory"], row, gridlistItems["inventory_colum_amount"], getElementData(getLocalPlayer(), item[1]), false, false) if getElementData(getLocalPlayer(), "currentweapon_2") and item[1] == getElementData(getLocalPlayer(), "currentweapon_2") then guiGridListSetItemColor(gridlistItems["inventory"], row, gridlistItems["inventory_colum"], 0, 255, 0) guiGridListSetItemColor(gridlistItems["inventory"], row, gridlistItems["inventory_colum_amount"], 0, 255, 0) end end end But i don't really know how can i start with it. Functions like getPlayerItems, getItemsFromElement etc. Link to comment
Tails Posted April 10, 2019 Share Posted April 10, 2019 (edited) Don't use metatables just for simple table lookups, that's only asking for problems and will decrease performance. In your case (response to original post) an int loop would be faster: for i=1, #playerTable.playerItems do local v = playerTable.playerItems[i] outputChatBox("item: "..v[1]..", count: "..v[2]) -- item: Item, count: 1 end However I can imagine that with a list of 1000's of items you wouldn't really want to loop this table over and over. What you're doing is, you're inserting a new table for every item and there isn't really any way to get the "Item" from the table with a single lookup other than looping the entire thing. What you could do is change the structure of the table to something like this: -- a fixed table that contains all the item ids and information local items = { ['apple'] = {desc = 'A green apple', name = 'apple'} -- an item with id 'apple' } local myItems = { ['apple'] = {count = 10}, -- player has an item with id 'apple' and has 10 of them } Now you'd still have to loop it if you wanted to get all the items at once but if you want to get the count of a specific item you can access it according to the item id. -- say you know the item id, all you do is local item = myItems['apple'] if item then print(item.count) end -- for instance function takeItem(id, count) local item = myItems[id] if item then if item.count >= count then item.count = item.count - count end end end -- if you want to get all then you loop it for id, item in pairs(myItems) do print('ID:', id, 'Count:', item.count, 'Description:', items[id].desc) end Notice you don't have to loop it in the takeItem function which will help with performance. So instead of table.insert you keep a key table where the id's are fixed and don't change so that you can access them by their key/id. Hope this helps. Edited April 10, 2019 by Tails 1 Link to comment
Moderators IIYAMA Posted April 11, 2019 Moderators Share Posted April 11, 2019 (edited) 12 hours ago, majqq said: But i don't really know how can i start with it. Functions like getPlayerItems, getItemsFromElement etc. For the items you can use the previous code. And this for attaching the data to elements: Instead of the enchanted table you can also use a regular table. But afaik it is a good match with your set-up. Note: this code works clientside as well as serverside. So keep in mind that you could write the system in a shared file. local elementDataCollection = {} function registerElement (element) local newTable = enchantedTable:create() elementDataCollection[element] = newTable end function getItemsFromElement (element) local thisTable = elementDataCollection[element] if thisTable then return thisTable:get() end return false end Edited April 11, 2019 by IIYAMA 1 Link to comment
Scripting Moderators ds1-e Posted April 12, 2019 Author Scripting Moderators Share Posted April 12, 2019 On 10/04/2019 at 22:45, Tails said: Don't use metatables just for simple table lookups, that's only asking for problems and will decrease performance. In your case (response to original post) an int loop would be faster: for i=1, #playerTable.playerItems do local v = playerTable.playerItems[i] outputChatBox("item: "..v[1]..", count: "..v[2]) -- item: Item, count: 1end However I can imagine that with a list of 1000's of items you wouldn't really want to loop this table over and over. What you're doing is, you're inserting a new table for every item and there isn't really any way to get the "Item" from the table with a single lookup other than looping the entire thing. What you could do is change the structure of the table to something like this: -- a fixed table that contains all the item ids and informationlocal items = { ['apple'] = {desc = 'A green apple', name = 'apple'} -- an item with id 'apple'}local myItems = { ['apple'] = {count = 10}, -- player has an item with id 'apple' and has 10 of them} Now you'd still have to loop it if you wanted to get all the items at once but if you want to get the count of a specific item you can access it according to the item id. -- say you know the item id, all you do islocal item = myItems['apple']if item then print(item.count)end-- for instancefunction takeItem(id, count) local item = myItems[id] if item then if item.count >= count then item.count = item.count - count end endend-- if you want to get all then you loop itfor id, item in pairs(myItems) do print('ID:', id, 'Count:', item.count, 'Description:', items[id].desc)end Notice you don't have to loop it in the takeItem function which will help with performance. So instead of table.insert you keep a key table where the id's are fixed and don't change so that you can access them by their key/id. Hope this helps. I would like to do it with this way, however how can i sort items later, in this order how they are placed in table? I can't use ipairs, because it loop with numbers, and pairs loop doesn't sort. Link to comment
Tails Posted April 12, 2019 Share Posted April 12, 2019 10 hours ago, majqq said: I would like to do it with this way, however how can i sort items later, in this order how they are placed in table? I can't use ipairs, because it loop with numbers, and pairs loop doesn't sort. You can use table.sort for sorting, but first you have to extract the data you want from the table and place it in another to make it iterable. For instance: local sorted = {} for itemId, itemData in pairs(myItems) do table.insert(sorted, itemId) -- extract item ids end -- sort the items table.sort(sorted, function(a, b) return a < b end) iprint(sorted) You can also sort it by the item count local sorted = {} for itemId, itemData in pairs(myItems) do table.insert(sorted, itemData) -- extract the item data instead end table.sort(sorted, function(a, b) return a.count < b.count end) As you can see you can extract any data from your tables and sort them however you like. 1 Link to comment
Scripting Moderators ds1-e Posted April 14, 2019 Author Scripting Moderators Share Posted April 14, 2019 On 10/04/2019 at 22:45, Tails said: Don't use metatables just for simple table lookups, that's only asking for problems and will decrease performance. In your case (response to original post) an int loop would be faster: for i=1, #playerTable.playerItems do local v = playerTable.playerItems[i] outputChatBox("item: "..v[1]..", count: "..v[2]) -- item: Item, count: 1end However I can imagine that with a list of 1000's of items you wouldn't really want to loop this table over and over. What you're doing is, you're inserting a new table for every item and there isn't really any way to get the "Item" from the table with a single lookup other than looping the entire thing. What you could do is change the structure of the table to something like this: -- a fixed table that contains all the item ids and informationlocal items = { ['apple'] = {desc = 'A green apple', name = 'apple'} -- an item with id 'apple'}local myItems = { ['apple'] = {count = 10}, -- player has an item with id 'apple' and has 10 of them} Now you'd still have to loop it if you wanted to get all the items at once but if you want to get the count of a specific item you can access it according to the item id. -- say you know the item id, all you do islocal item = myItems['apple']if item then print(item.count)end-- for instancefunction takeItem(id, count) local item = myItems[id] if item then if item.count >= count then item.count = item.count - count end endend-- if you want to get all then you loop itfor id, item in pairs(myItems) do print('ID:', id, 'Count:', item.count, 'Description:', items[id].desc)end Notice you don't have to loop it in the takeItem function which will help with performance. So instead of table.insert you keep a key table where the id's are fixed and don't change so that you can access them by their key/id. Hope this helps. On 11/04/2019 at 08:49, IIYAMA said: For the items you can use the previous code. And this for attaching the data to elements: Instead of the enchanted table you can also use a regular table. But afaik it is a good match with your set-up. Note: this code works clientside as well as serverside. So keep in mind that you could write the system in a shared file. local elementDataCollection = {}function registerElement (element) local newTable = enchantedTable:create() elementDataCollection[element] = newTableendfunction getItemsFromElement (element) local thisTable = elementDataCollection[element] if thisTable then return thisTable:get() end return falseend Thank you guys for help. 1 Link to comment
Scripting Moderators ds1-e Posted May 24, 2019 Author Scripting Moderators Share Posted May 24, 2019 (edited) Hey, once again. I was trying to sort items in table, then show them in GUI, but couldn't make it work. I would like to sort it alphabetically or by value given for certain item. Is there any way to get it to work, for key indexes, without doing any serious changes? Because it took me some time to finish functions for this table structure. ["First Aid Kit"] = {slots = 2, category = "Medicine"}, Edited May 24, 2019 by majqq Link to comment
Moderators IIYAMA Posted May 24, 2019 Moderators Share Posted May 24, 2019 6 hours ago, majqq said: Hey, once again. I was trying to sort items in table, then show them in GUI, but couldn't make it work. I would like to sort it alphabetically or by value given for certain item. Is there any way to get it to work, for key indexes, without doing any serious changes? Because it took me some time to finish functions for this table structure. ["First Aid Kit"] = {slots = 2, category = "Medicine"}, There are no serious changes required for the table. But it might be handy to create an array structure first and then convert it to an object structure. This is the basic: The table local theTable = { ["First Aid Kit"] = {slots = 24, category = "Medicine"}, ["First Aid Kit2"] = {slots = 46, category = "Medicine"}, ["First Aid Kit3"] = {slots = 8, category = "Medicine"}, } Convert to an array structure: local collection = {} for key, data in pairs (theTable) do collection[#collection + 1] = {key = key, data = data} end Sort the damn thing: function sortFunction (a, b) return a.slots < b.slots end table.sort(collection, function (a, b) return sortFunction(a.data, b.data) end) For each: function forEach (key, data) print(key, data) end for i=1, #collection do local item = collection[i] forEach(item.key, item.data) end Which can enchanted with Lua OOP. (not MTA OOP) But as far as I know you do not work with it, never the less: Spoiler function makeSortAbleTable (theTable) local sortAbleTable = { collection = {}, forEach = function (self, theFunction) local collection = self.collection for i=1, #collection do local item = collection[i] theFunction(item.key, item.data) end end, sort = function (self, theFunction) table.sort(self.collection, function (a, b) return theFunction(a.data, b.data) end) end } local collection = sortAbleTable.collection for key, data in pairs (theTable) do collection[#collection + 1] = {key = key, data = data} end return sortAbleTable end local theTable = { ["First Aid Kit"] = {slots = 24, category = "Medicine"}, ["First Aid Kit2"] = {slots = 46, category = "Medicine"}, ["First Aid Kit3"] = {slots = 8, category = "Medicine"}, } local sortAbleTable = makeSortAbleTable (theTable) sortAbleTable:sort(function (a, b) print (a, b) return a.slots < b.slots end) sortAbleTable:forEach(function (key, data) print(key, data) end) 1 Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now