Check if index in table exist - indexing

I have problem; I must check in my program one field in table.
if(launchArgs.androidIntent.extras.notification.custom.test_field ~= nil)then...
and when this index exist everything is ok, but when it isn't exist, I get an error :
Attempt to index field 'notification' (a nil value).
And it is understandable. How check if that index exist?

Try this
if (launchArgs and launchArgs.androidIntent and launchArgs.androidIntent.extras
and launchArgs.androidIntent.extras.notification and launchArgs.androidIntent.extras.notification.custom
and launchArgs.androidIntent.extras.notification.custom.test_field) then
-- do you stuff
end
This code will check if each table is set.
If you're sure launch args.androidIntent.extras is always set you can just do this
if(launchArgs.androidIntent.extras.notification and launchArgs.androidIntent.extras.notification.custom and launchArgs.androidIntent.extras.notification.custom.test_field)then
-- do your stuff
end
OR Just use this function, that I posted in some other answer (helps here too )
function IndexScan(input,value,case,_type)
if (input and type(input) == 'table') then
if (_type) then
if (type(value) == _type and value == input) then
return true;
end
else
if (type(value) == 'table' and value == input) then
return true;
end
end
for key,object in pairs(input) do
if (case and type(input)=='string' and type(key)=='string') then
if (_type) then
if (value:lower() == key:lower() and type(object)==_type) then
return true;
elseif(type(object)=='table') then
return IndexScan(object,value,case,_type)
end
else
if (value:lower() == key:lower()) then
return true;
elseif(type(object)=='table') then
return IndexScan(object,value,case,_type)
end
end
else
if (_type) then
if (key == value and type(object)==_type) then
return true
elseif(type(object)=='table') then
return IndexScan(object,value,case,_type)
end
else
if (key == value) then
return true
elseif(type(object)=='table') then
return IndexScan(object,value,case,_type)
end
end
end
end
end
return false;
end
-- IndexScan(#param table(table), #param index(string), #param case-sensitive(true/false), #param type (index type, string/boolean/number/table ...))
-- checks if these two indexes were set any where in the launchArgs table and checks their type
if (IndexScan(launchArgs,"notification",false,"table") and IndexScan(launchArgs,"test_field",false,"string")) then
-- do your stuff
end
EDIT:
Fixed some mistake in the function.
EDIT:
Updated the script after the author fixed the Notification typo.

Try also this:
function setglobal(name,value)
local t=_ENV
local f="_G"
for x in name:gmatch("[^.]+") do
if t[f]==nil then t[f]={} end
t=t[f]
f=x
end
t[f]=value
end
function getglobal(name)
local t=_ENV
for x in name:gmatch("[^.]+") do
t=t[x]
if t==nil then return nil,x end
end
return t
end
setglobal("launchArgs.androidIntent.extras.notification.custom.test_field",2014)
print(getglobal("launchArgs.androidIntent.extras.notification.custom.test_field"))
print(getglobal("launchArgs.androidIntent.extras.notifiaction.custom.test_field"))
This assumes that the top-level variable is a global variable. Adapt as needed.

You can use this:
local function try(root, query)
local ids, len = {}, 0
for id in query:gmatch("%w+") do
len = len + 1
ids[len]= id
end
local node = root
for i=1,len do
if type(node) ~= 'table' then return nil end
node = node[ids[i]]
end
return node
end
Usage:
local tbl = { a = { b = { c = { d = 1 } } } }
print(try(tbl, 'a.b.c.d')) -- prints 1
print(try(tbl, 'a.b.c.x')) -- prints nil
print(try(tbl, 'a.x.c.d')) -- prints nil

Related

Lua implementation of BestSum function using memoization

I am trying to translate the below javascript "bestSum" memoization function into lua:
const bestSum = (targetSum,numbers,memo ={}) => {
if(targetSum in memo) return memo[targetSum];
if(targetSum === 0 ) return [];
if(targetSum <0)return null;
let shortestCombination = null;
for (let num of numbers) {
const remainder = targetSum - num;
const remainderCombination = bestSum(remainder,numbers,memo);
if (remainderCombination !==null) {
const combination = [...remainderCombination, num];
if (shortestCombination === null || combination.length < shortestCombination.length)
{
shortestCombination = combination;
}
}
}
memo [targetSum] = shortestCombination;
return shortestCombination;
}
sample test cases with correct results:
console.log(bestSum(7,[5,3,4,7])); //[7]
console.log(bestSum(8,[2,3,5])); //[3,5]
console.log(bestSum(8,[1,4,5])); //[4,4]
console.log(bestSum(100,[1,2,5,25])); //[25,25,25,25]
I translated the above javascript into lua as the following:
local function BestSum(target_sum,numbers,memo)
if memo[target_sum] ~= nil then return memo[target_sum] end
if target_sum == 0 then return {} end
if target_sum < 0 then return nil end
local shortest_combination = nil
for i, num in ipairs (numbers) do
local remainder = target_sum - num
local remainder_combination = BestSum(remainder,numbers, memo)
if remainder_combination ~= nil then
local combination = remainder_combination
table.insert(combination,num )
if (shortest_combination == nil) or (#combination < #shortest_combination )then
shortest_combination = combination
end
end
end
memo[target_sum] = shortest_combination;
return shortest_combination;
end
but don't get the desired results for the two last cases...... instead get incorrect results:
BestSum(8,{1,4,5},{})==>{"4","1","4"}
BestSum(150,{5,25},{})==>
{"25","5","5","5","5","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25","5","25"}
The results are not even correct let alone being "best" case??
Can anyone spot where I'm going wrong?
Much appreciated
The problem is with this part of the translation:
local combination = remainder_combination
table.insert(combination, num)
Tables are pass by reference, so this isn't creating a new table, it's just assigning the variable combination to the same table. Modifying combination is just adding more data to remainder_combination.
The JavaScript version is taking care to create a new array, and fills it with the contents of the remainderCombination array (using '...', the spread operator):
const combination = [...remainderCombination, num];
This is the most accurate Lua translation:
local combination = {unpack(remainder_combination)}
table.insert(combination, num)
(Edit: For Lua 5.2+ it's table.unpack)

Is there anyway to make this function better for my module?

I've been recently messing around with modules and I wanted to know if there's anyway to make this calculate function any better?
function Library.calc(arg1,arg2,option)
local options = {
[1] = "add",
[2] = "sub",
[3] = "mul",
[4] = "div"
}
if option == options[1] then
print(arg1+arg2)
end
if option == options[2] then
print(arg1-arg2)
end
if option == options[3] then
print(arg1*arg2)
end
if option == options[4] then
print(arg1/arg2)
end
end
This question should be migrated to Code Review SE, as Nifim pointed out.
local options = {
add = function (x,y) return x+y end,
sub = function (x,y) return x-y end,
mul = function (x,y) return x*y end,
div = function (x,y) return x/y end,
}
function Library.calc(arg1,arg2,option)
print(assert(options[option], "Unknown option")(arg1, arg2))
end

Return table value index via metatable

I'm looking for a way to retrive index value via metatable. This is my attempt:
local mt = { __index =
{
index = function(t, value)
local value = 0
for k, entry in ipairs(t) do
if (entry == value) then
value = k
end
end
return value
end
}
}
t = {
"foo", "bar"
}
setmetatable(t,mt)
print(t.index(t,"foo"))
Result is 0 instead of 1. Where I'm wrong?
My attempt:
local mt = {
__index = function(t,value)
for index, val in pairs(t) do
if value == val then
return index
end
end
end
}
t = {
"foo",
"bar",
"aaa",
"bbb",
"aaa"
}
setmetatable(t,mt)
print(t["aaa"]) -- 3
print(t["asd"]) -- nil
print(t["bbb"]) -- 4
print(t["aaa"]) -- 3
print(t["bar"]) -- 2
print(t["foo"]) -- 1
Result is 0 instead of 1. Where [am I] wrong?
The code for the index function is wrong; the problem is not related to the (correct) metatable usage. You're shadowing the parameter value when you declare local value = 0. Subsequent entry == value comparisons yield false as the strings don't equal 0. Rename either the parameter or the local variable:
index = function(t, value)
local res = 0
for k, entry in ipairs(t) do
if entry == value then
res = k
end
end
return res
end
An early return instead of using a local variable in the first place works as well and helps improve performance.
To prevent such errors from happening again, consider getting a linter like Luacheck, which will warn you if you shadow variables. Some editors support Luacheck out of the box; otherwise there are usually decent plugins available.

Lua reference table inside metatable

I have a pretty mind-bending setup right now. I have a regular function that returns a table with functions in it under keys "string" and "number":
function defGeneric()
local function funcNumber(a)
return 2*a^2
end
local function funcString(a)
return a.." - test"
end
local returnTable={}
returnTable["number"]=funcNumber
returnTable["string"]=funcString
return returnTable
end
And that works fine. But what I want to do now is make the table that this function returns callable. To illustrate, let's say we have v=defGeneric(). Specifically:
If v is called with a string str, return the result of v["string"](str)
If v is called with a number n, return the result of v["number"](n)
This is obviously a job for metatables, so I can (in my function) add the code to set a metatable:
local metaTable = {
__call = function (...) -- "call" event handler
return
end
}
setmetatable(returnTable,metaTable)
But I don't know what I would put after that return statement. I don't think I can reference returnTable, because this table will be called like so:
v=defGeneric()
v("test")
And I need to reference v's "string" function (there certainly could be multiple defGeneric() tables in one program).
I think the answer here might be some self trick but I can't wrap my head around how. How do I reference a metatable's table from the metatable?
The first argument passed to the __call function is the table it is being called on, the table returned from the function in this case. You can use type(a) to get the type of the argument as a string, so you could do something like this:
function defGeneric()
local result = {
['number'] = function(a) return 2*a^2 end,
['string'] = function(a) return a.." - test" end
}
setmetatable(result, {
__call = function(t,a)
local f = t[type(a)]
if f == nil then return "No handler for type "..type(a) end
-- alternate:
-- if f == nil and t['string'] ~= nil then return t['string'](tostring(a)) end
return f(a)
end
})
return result
end
local def = defGeneric()
print("string: "..tostring(def('sample string')))
print("number: "..tostring(def(5)))
print("table: "..tostring(def({})))
print("boolean: "..tostring(def(1 > 5)))
output
string: sample string - test
number: 50.0
table: No handler for type table
boolean: No handler for type boolean
alternate output
string: sample string - test
number: 50.0
table: table: 0x18537e0 - test
boolean: false - test

attempt to call method 'print' (a nil value) when implementation OOP in Lua

So, I'm trying to write a simple class in Lua for representing CSV fields:
csv_entry = {}
csv_entry.__index = csv_entry
function csv_entry.create(...)
return arg
end
function csv_entry:tostring()
local str = string.char()
for i,v in ipairs(self) do
if i < #self then
str = str .. v
else
str = str .. v .. ", "
end
end
end
function csv_entry:print()
print(self:tostring())
end
But when I try to use this class like this:
c = csv_entry.create("Volvo", 10000, "Eric")
c:print() -- line 25
I get the error message
lua: csv.lua:25: attempt to call method 'print' (a nil value)
And I can't really figure out the issue here. What am I doing wrong?
You probably meant to do is this:
function csv_entry.create(...)
return setmetatable(arg, csv_entry)
end
Your posted version of cvs_entry.create just returns it's arguments packed into a table, so this code:
c = csv_entry.create("Volvo", 10000, "Eric")
c:print()
Is exactly equivalent to this code:
c = {"Volvo", 10000, "Eric"}
c:print()
c doesn't contain a print entry, so c.print returns nil and c:print() fails because you're trying to "call" nil.
Side note: the implicit arg parameter to variadic functions was removed in Lua 5.1 (6 years ago). The correct way to do this now is:
function csv_entry.create(...)
local arg = {...}
return setmetatable(arg, csv_entry)
end
Or simply:
function csv_entry.create(...)
return setmetatable({...}, csv_entry)
end
As long as we're here: you're going to get no output from csv_entry:tostring because it doesn't return anything. Also, if all you're trying to do is to concatenate a bunch of items with comma separators, you can use table.concat:
function csv_entry:tostring()
return table.concat(self, ', ')
end
I rewrite your code to meet what it is for, it runs OK for me:
csv_entry = {}
function csv_entry:create(...)
o = {content = {}}
self.__index = self;
setmetatable(o, self)
for i = 1, arg.n do
o.content[i] = arg[i];
end
return o;
end
function csv_entry:tostring()
local resStr = ""
for i, v in pairs(self.content) do
resStr = resStr .. v;
if i < #(self.content) then
resStr = resStr .. ", "
end
end
return resStr;
end
function csv_entry:print()
print(self:tostring())
end
c = csv_entry:create("Volvo", 10000, "Eric")
c:print()
Like #Mud said, the create(...) in your code is just a regular call and returns all arguments from ..., if you want csv_entry works like a class, then you have to put codes which set metatable and __index into create(...), and return an instance from csv_entry class