Checking string containing item from table - lua

if FirstName:GetValue() == ""
or table.HasValue( Blocked, string.lower(FirstName:GetValue())) then
-- Checks if the name contains any "bad" words, or nothing was typed in.
FirstNameCHECK = false
text4:SetText("Bad Name")
text4:SetColor(Color(255,0,0,255))
else
FirstNameCHECK = true
text4:SetText("Good Name")
text4:SetColor(Color(0,255,0,255))
end
This code currently checks for the string being exactly the same as an entry in a table.
How would I be able to change this code so it checks if the string inserted (FirstName variable) contains one of the entries from the table?

The inefficient solution would be to iterate over the table and call string.find on each element. This approach can get quite slow for very large tables, as you have to inspect every element, but will probably be perfectly fine unless you are dealing with really big datasets.
A more clever approach would be to use nested tables indexed by substrings. For instance, you could have a root table with indices a to z. You index into that table with the first letter of your word and you get back another table of the same structure. This one you index with the second letter and so on until you either find no more table at the letter you are checking, or you arrived at the end of the word. In the latter case, you might need an additional unique entry in the table that indicates whether the exact word you were looking up is in the table (as there are no more letters, you need to be able to check this somehow).
This approach has several disadvantages. Building the table can be very memory intensive and performing the check might very well be slower than the naive approach for smaller tables. Also manipulating the lookup table is not that straightforward (think about removing a word from it for instance). So this kind of structure is really only useful for performing the lookup and you should stick with normal tables for other tasks.
For example, you might want to maintain in the lookup table a list of references to entries in the real data table which allow you to get from a particular prefix string all matching entries in the data table.

Made a solution for those kind of stuff in some other question (works with multi dimensional tables) - Loop until find 2 specific values in a table?
function MultiTableSearch(input,value,case,index_check)
if (input and type(input) == 'table') then
if (type(value) == 'table' and value == input) then
return true;
end
for key,object in pairs(input) do
if (index_check) then
if (case and type(input)=='string' and type(key)=='string') then
if (value:lower() == key:lower()) then -- to avoid exit the loop
return true;
end
else
if (key == value) then
return true
elseif(type(object)=='table') then
return MultiTableSearch(object,value,case,index_check)
end
end
else
if (case and type(value)=='string' and type(object) == 'string') then
if (value:lower() == object:lower()) then
return true;
end
elseif(type(object)=='table') then
if (value == object) then
return true;
else
return MultiTableSearch(object,value,case)
end
else
if (object == value) then
return true;
end
end
end
end
end
return false;
end
and to use call this
MultiTableSearch(blocked,FirstName:GetValue(),true) -- table,name,case(true to ignore case)

Related

Performance of accessing table via reference vs ipairs loop

I'm modding a game. I'd like to optimize my code if possible for a frequently called function. The function will look into a dictionary table (consisting of estimated 10-100 entries). I'm considering 2 patterns a) direct reference and b) lookup with ipairs:
PATTERN A
tableA = { ["moduleName.propertyName"] = { some stuff } } -- the key is a string with dot inside, hence the quotation marks
result = tableA["moduleName.propertyName"]
PATTERN B
function lookup(type)
local result
for i, obj in ipairs(tableB) do
if obj.type == "moduleName.propertyName" then
result = obj
break
end
end
return result
end
***
tableB = {
[1] = {
type = "moduleName.propertyName",
... some stuff ...
}
}
result = lookup("moduleName.propertyName")
Which pattern should be faster on average? I'd expect the 'native' referencing to be faster (it is certainly much neater), but maybe this is a silly assumption? I'm able to sort (to some extent) tableB in a order of frequency of the lookups whereas (as I understand it) tableA will have in Lua random internal order by default even if I declare the keys in proper order.
A lookup table will always be faster than searching a table every time.
For 100 elements that's one indexing operation compared to up to 100 loop cycles, iterator calls, conditional statements...
It is questionable though if you would experience a difference in your application with so little elements.
So if you build that data structure for this purpose only, go with a look-up table right away.
If you already have this data structure for other purposes and you just want to look something up once, traverse the table with a loop.
If you have this structure already and you need to look values up more than once, build a look up table for that purpose.

How to iterate over an ActiveRecord resultset in one line with nil check in Ruby

I have an array of Active Record result and I want to iterate over each record to get a specific attribute and add all of them in one line with a nil check. Here is what I got so far
def total_cost(cost_rec)
total= 0.0
unless cost_rec.nil?
cost_rec.each { |c| total += c.cost }
end
total
end
Is there an elegant way to do the same thing in one line?
You could combine safe-navigation (to "hide" the nil check), summation inside the database (to avoid pulling a bunch of data out of the database that you don't need), and a #to_f call to hide the final nil check:
cost_rec&.sum(:cost).to_f
If the cost is an integer, then:
cost_rec&.sum(:cost).to_i
and if cost is a numeric inside the database and you don't want to worry about precision issues:
cost_rec&.sum(:cost).to_d
If cost_rec is an array rather than a relation (i.e. you've already pulled all the data out of the database), then one of:
cost_rec&.sum(&:cost).to_f
cost_rec&.sum(&:cost).to_i
cost_rec&.sum(&:cost).to_d
depending on what type cost is.
You could also use Kernel#Array to ignore nils (since Array(nil) is []) and ignore the difference between arrays and ActiveRecord relations (since #Array calls #to_ary and relations respond to that) and say:
Array(cost_rec).sum(&:cost)
that'll even allow cost_rec to be a single model instance. This also bypasses the need for the final #to_X call since [].sum is 0. The downside of this approach is that you can't push the summation into the database when cost_rec is a relation.
anything like these?
def total_cost(cost_rec)
(cost_rec || []).inject(0) { |memo, c| memo + c.cost }
end
or
def total_cost(cost_rec)
(cost_rec || []).sum(&:cost)
end
Either one of these should work
total = cost_rec.map(&:cost).compact.sum
total = cost_rec.map{|c| c.cost }.compact.sum
total = cost_rec.pluck(:cost).compact.sum
Edit: if cost_rec is nil
total = (cost_rec || []).map{|c| c.cost }.compact.sum
When cost_rec is an ActiveRecord::Relatation then this should work out of the box:
cost_rec.sum(:cost)
See ActiveRecord::Calculations#sum.

table.insert -> remember key of inserted value

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.

How to compare a bunch of values with themselves

I have a list of indexes I need my users to input, and I need the code to check if any of them are repeated, so it would give an error (they cannot be repeat).
if i only had two indexes it would be simple as :
if indexa == indexb then error() end
but its a fairly long list.
Here's a basic algorithm for detecting repeats.
-- This table is what's known as a set.
local indexes = {}
while true do
local index = getIndexFromUser()
-- Check for end of input.
if not index then
break
end
-- Check for repeats.
if indexes[index] then
error()
end
-- Store index as a key in indexes.
indexes[index] = true
end
In other words, table keys cannot be repeated, so you can simply store any non-nil value in a table under that key. Later (in future iterations of the loop), you can check to see whether that key is nil.
You could put all the indices in a table, use table.sort to sort them, then loop over table items to test if any consecutive items are identical:
indices = {1,6,3,0,3,5} -- will raise error
indices = {1,6,3,0,4,5} -- will not raise error
table.sort(indices)
for i=1, (#indices-1) do
if indices[i] == indices[i+1] then
error('not allowed duplicates')
end
end

How to check if a table contains an element in Lua?

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

Resources