Jump to content

Check if table contains something


ds1-e

Recommended Posts

  • Scripting Moderators

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

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.

  • Like 1
Link to comment
  • Scripting Moderators
  On 28/03/2019 at 14:32, 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.

Expand  

But this is good way to do it, or it's a better soluton for it?

  • Like 1
Link to comment
  • Moderators
  On 28/03/2019 at 22:06, majqq said:

But this is good way to do it, or it's a better soluton for it?

Expand  

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)

  • Like 1
Link to comment
  • Moderators

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

 

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.

 

  • Like 2
Link to comment
  • 2 weeks later...
  • Scripting Moderators
  On 29/03/2019 at 19: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

 

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.

 

Expand  

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

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 by Tails
  • Like 1
Link to comment
  • Moderators
  On 10/04/2019 at 18:41, majqq said:

But i don't really know how can i start with it. Functions like getPlayerItems, getItemsFromElement etc. 

Expand  

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 by IIYAMA
  • Like 1
Link to comment
  • Scripting Moderators
  On 10/04/2019 at 20: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.

Expand  

 

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
  On 12/04/2019 at 01:22, 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.

Expand  

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.

 

  • Like 1
Link to comment
  • Scripting Moderators
  On 10/04/2019 at 20: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.

Expand  

 

  On 11/04/2019 at 06: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

 

 

Expand  

 

Thank you guys for help.

  • Like 1
Link to comment
  • 1 month later...
  • Scripting Moderators

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 by majqq
Link to comment
  • Moderators

 

  On 24/05/2019 at 13:41, 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"},

 

Expand  

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:

  Reveal hidden contents

 

 

 

 

 

 

 

  • Like 1
Link to comment

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...