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
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?

  • Like 1
Link to comment
  • Moderators
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)

  • 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.

 

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.

 

  • Like 2
Link to comment
  • 2 weeks later...
  • Scripting Moderators
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

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
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 by IIYAMA
  • Like 1
Link to comment
  • Scripting Moderators
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
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.

 

  • Like 1
Link to comment
  • Scripting Moderators
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.

  • 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

 

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)

 

 

 

 

 

 

 

 

  • 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...