Lua: Functions with Tables for Beginners - Proper Naming/Retrieving of Tables within Tables - function

I am having a horrible time at grasping functions and tables. I've asked a question before that is similar to this but still am having problems getting this to work properly. So I will be more descriptive. But just when I think I understand it I completely confuse myself again. Here is what I am trying to accomplish:
I have a program that is receiving its input from an outside source. It needs to take that input, and basically "dissect" the strings to get the required information. Based on the information it receives, it moves onto the next phase or functions to do the appropriate actions. For example:
input is received as NY345,de,M,9900
I created a table that has all of the different ways the specific input can begin, such as:
local t = {["NY"] = 5, ["MS"] = 7, ["HG"] = 10, ["JX"] = 14, ["UY"] = 20}
Now I want to use a function to receive the input and look for k in t{} and use that to gather other variables...
function seek(input)
for k, v in pairs (seek) do
local info = string.match(input,k)
if info then
return {seekData = string.match(input,k..",(%d*),.*"), seekMult = seekData*v}
end
end
end
How far off am I?
If I had the table "t = {...}" above, and that contained other tables; how can I name each table inside of "t = {...}" and retrieve it for other equations? Such as if ["a"] = 8, the rest of that table was to be utilized? For example:
t={["a"] = 2, ["b"] = 3, ["c"] = "IOS"},{["a"] = 8, ["b"] = 9, ["c"] = "NVY"},{["a"] = 1, ["b"] = 5, ["c"] = "CWQ"}}
if a = 8, then b = 9 and c = "NVY"
I would like my function to search k (of each table) and compare it with the input. If that was found, then it would set the other two local variables to b and c?
Thanks for your help!

I will only answer question 1, as 2 and 3 should be separate questions. There are many ways to do this based on specifics you don't mention but assuming you have a table t like this:
t={
{["a"] = 2, ["b"] = 3, ["c"] = "IOS"},
{["a"] = 8, ["b"] = 9, ["c"] = "NVY"},
{["a"] = 1, ["b"] = 5, ["c"] = "CWQ"}
}
then a function that takes an a key value to look for and returns b and c:
function findItem(a, yourTable)
for i,tt in ipairs(yourTable) do
if tt.a == a then
return i, tt.b, tt.c
end
end
end
With this, if the input is k, then
i, b, c = findItem(k, t)
if i == nil then
print('could not find k')
else
print('found k at index ' .. i)
end
The findItem could of course just return the subtable found, and maybe you don't need index:
function findItem(a, yourTable)
for i,tt in ipairs(yourTable) do
if tt.a == a then
return tt
end
end
end

Related

I need to make a dynamic aggregation in Power Query, by summing or concatenating the duplicated values in my tables

Here's an example of my data:
Sample
Method A
Method B
Method C
Method D
Method E
BATCH Nu
Lab Data
Sample 1
1
2
8
TX_0001
LAB1
Sample 1
5
9
TX_0002
LAB2
Sample 2
7
8
8
23
TX_0001
LAB1
Sample 2
41
TX_0001
LAB2
Sample 3
11
55
TX_0394
LAB2
Sample 4
2
9
5
9
TX_0394
LAB1
I need to make a M Language code that unites them, based on duplicated samples. Note that they might be in the same batch and/or in the same lab, but they won't ever be made the same method twice.
So I can't pass the column names, because they keep changing, and I wanted to do it passaing the column names dynamically.
**OBS: I have the possibility to make a linked table of the source to a Microsoft Access and make this with SQL, but I couldn't find a text aggregation function in MS Access library. There it's possible to each column name with no problem. (Just a matter that no one else knows M Language in my company and I can't let this be non-automated)
**
This is the what I have been trying to improve, but I keep have some errors:
1.Both goruped columns have "Errors" in all of the cells
2.Evaluation running out of memory
I can't discover what I'm doing wrong here.
let
Source = ALS,
schema = Table.Schema(Source),
columns = schema[Name],
types = schema[Kind],
Table = Table.FromColumns({columns,types}),
Number_Columns = Table.SelectRows(Table, each ([Column2] = "number")),
Other_Columns = Table.SelectRows(Table, each ([Column2] <> "number")),
numCols = Table.Column(Number_Columns, "Column1"),
textColsSID = List.Select(Table.ColumnNames(Source), each Table.Column(Source, _) <> type number),
textCols = List.RemoveItems(textColsSID, {"Sample ID"}),
groupedNum = Table.Group(Source, {"Sample ID"},List.Transform(numCols, each {_, (nmr) => List.Sum(nmr),type nullable number})),
groupedText = Table.Group(Source,{"Sample ID"},List.Transform(textCols, each {_, (tbl) => Text.Combine(tbl, "_")})),
merged = Table.NestedJoin(groupedNum, {"Sample ID"}, groupedText, {"Sample ID"}, "merged"),
expanded = Table.ExpandTableColumn(merged, "merged", Table.ColumnNames(merged{1}[merged]))
in
expanded
This is what I expected to have:
Sample
Method A
Method B
Method C
Method D
Method E
BATCH Nu
Lab Data
Sample 1
1
2
5
9
8
TX_0001_TX_0002
LAB1_LAB2
Sample 2
7
8
8
23
41
TX_0001_TX_0001
LAB1_LAB1
Sample 3
11
55
TX_0394
LAB2
Sample 4
2
9
5
9
TX_0394
LAB1
Here is a method which assumes only that the first column is a column which will be used to group the different samples.
It makes no assumptions about any column names, or the numbers of columns.
It tests the first 10 rows in each column (after removing any nulls) to determine if the column type can be type number, otherwise it will assume type text.
If there are other possible data types, the type detection code can be expanded.
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
//dynamically detect data types from first ten rows
//only detecting "text" and "number"
colNames = Table.ColumnNames(Source),
checkRows = 10,
colTestTypes = List.Generate(
()=>[t=
let
Values = List.FirstN(Table.Column(Source,colNames{0}),10),
tryNumber = List.Transform(List.RemoveNulls(Values), each (try Number.From(_))[HasError])
in
tryNumber, idx=0],
each [idx] < List.Count(colNames),
each [t=
let
Values = List.FirstN(Table.Column(Source,colNames{[idx]+1}),10),
tryNumber = List.Transform(List.RemoveNulls(Values), each (try Number.From(_))[HasError])
in
tryNumber, idx=[idx]+1],
each [t]),
colTypes = List.Transform(colTestTypes, each if List.AllTrue(_) then type text else type number),
//Group and Sum or Concatenate columns, keying on the first column
group = Table.Group(Source,{colNames{0}},
{"rw", (t)=>
Record.FromList(
List.Generate(
()=>[rw=if colTypes{1} = type number
then List.Sum(Table.Column(t,colNames{1}))
else Text.Combine(Table.Column(t,colNames{1}),"_"),
idx=1],
each [idx] < List.Count(colNames),
each [rw=if colTypes{[idx]+1} = type number
then List.Sum(Table.Column(t,colNames{[idx]+1}))
else Text.Combine(Table.Column(t,colNames{[idx]+1}),"_"),
idx=[idx]+1],
each [rw]), List.RemoveFirstN(colNames,1)), type record}
),
//expand the record column and set the data types
#"Expanded rw" = Table.ExpandRecordColumn(group, "rw", List.RemoveFirstN(colNames,1)),
#"Set Data Type" = Table.TransformColumnTypes(#"Expanded rw", List.Zip({colNames, colTypes}))
in
#"Set Data Type"
Original Data
Results
One way. You could probably do this all within the group as well
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
names = List.Distinct(List.Select(Table.ColumnNames(Source), each Text.Contains(_,"Method"))),
#"Grouped Rows" = Table.Group(Source, {"Sample"}, {{"data", each _, type table }}),
#"Added Custom" = Table.AddColumn(#"Grouped Rows", "Batch Nu", each Text.Combine(List.Distinct([data][BATCH Nu]),"_")),
#"Added Custom1" = Table.AddColumn(#"Added Custom", "Lab Data", each Text.Combine(List.Distinct([data][Lab Data]),"_")),
#"Added Custom2" = Table.AddColumn(#"Added Custom1", "Custom", each Table.SelectRows(Table.UnpivotOtherColumns([data], {"Sample"}, "Attribute", "Value"), each List.Contains(names,[Attribute]))),
#"Added Custom3" = Table.AddColumn(#"Added Custom2", "Custom.1", each Table.Pivot([Custom], List.Distinct([Custom][Attribute]), "Attribute", "Value", List.Sum)),
#"Expanded Custom.1" = Table.ExpandTableColumn(#"Added Custom3" , "Custom.1", names,names),
#"Removed Columns" = Table.RemoveColumns(#"Expanded Custom.1",{"data", "Custom"})
in #"Removed Columns"

COUNTIFS: Excel to pandas and remove counted elements

I have a COUNTIFS equation in excel (COUNTIFS($A$2:$A$6, "<=" & $C4))-SUM(D$2:D3) where A2toA6 is my_list. C4 is current 'bin' with the condition and D* are previous summed results from my_list that meet the condition. I am attempting to implement this in Python
I have looked at previous COUNTIF questions but I am struggling to complete the final '-SUM(D$2:D3)' part of the code.
See the COUNTIFS($A$2:$A$6, "<=" & $C4) section below.
'''
my_list=(-1,-0.5, 0, 1, 2)
bins = (-1, 0, 1)
out = []
for iteration, num in enumerate(bins):
n = []
out.append(n)
count = sum(1 for elem in my_list if elem<=(num))
n.append(count)
print(out)
'''
out = [1, [3], [4]]
I need to sum previous elements, that have already been counted, and remove these elements from the next count so that they are not counted twice ( Excel representation -SUM(D$2:D3) ). This is where I need some help! I used enumerate to track iterations. I have tried the code below in the same loop but I can't resolve this and I get errors:
'''
count1 = sum(out[0:i[0]]) for i in (out)
and
count1 = out(n) - out(n-1)
''''
See expected output values in 'out' array for bin conditions below:
I was able to achieve the required output array values by creating an additional if/elif statement to factor out previous array elements and generate a new output array 'out1'. This works but may not be the most efficient way to achieve the end goal:
'''
import numpy as np
my_list=(-1,-0.5, 0, 1, 2)
#bins = np.arange(-1.0, 1.05, 0.05)
bins = (-1, 0, 1)
out = []
out1 = []
for iteration, num in enumerate(bins):
count = sum(1 for elem in my_list if elem<=(num))
out.append(count)
if iteration == 0:
count1 = out[iteration]
out1.append(count1)
elif iteration > 0:
count1 = out[iteration] - out[iteration - 1]
out1.append(count1)
print(out1)
'''
I also tried using the below code as suggested in other answers but this didn't work for me:
'''
-np.diff([out])
print(out)
'''

Use varargin for multiple arguments with default values in MATLAB

Is there a way to supply arguments using varargin in MATLAB in the following manner?
Function
func myFunc(varargin)
if a not given as argument
a = 2;
if b not given as argument
b = 2;
if c not given as argument
c = a+b;
d = 2*c;
end
I want to call the above function once with b = 3 and another time while the previous one is running in the same command window with a = 3 and c = 3 and letting b take the default value in the function this time. How can it be done using varargin?
Here's the latest and greatest way to write the function (using arguments blocks from R2019b)
function out = someFcn(options)
arguments
options.A = 3;
options.B = 7;
options.C = [];
end
if isempty(options.C)
options.C = options.A + options.B;
end
out = options.A + options.B + options.C;
end
Note that this syntax does not allow you to say options.C = options.A + options.B directly in the arguments block.
In MATLAB < R2021a, you call this like so
someFcn('A', 3)
In MATLAB >= R2021a, you can use the new name=value syntax
someFcn(B = 7)
Here are two ways to do this which have been available since 2007a (i.e. a long time!). For a much newer approach, see Edric's answer.
Use nargin and ensure your inputs are always in order
Use name-value pairs and an input parser
nargin: slightly simpler but relies on consistent input order
function myFunc( a, b, c )
if nargin < 1 || isempty(a)
a = 2;
end
if nargin < 2 || isempty(b)
b = 2;
end
if nargin < 3 || isempty(c)
c = a + b;
end
end
Using the isempty check you can optionally provide just later arguments, for example myFunc( [], 4 ) would just set b=4 and use the defaults otherwise.
inputParser: more flexible but can't directly handle the c=a+b default
function myFunc( varargin )
p = inputParser;
p.addOptional( 'a', 2 );
p.addOptional( 'b', 2 );
p.addOptional( 'c', NaN ); % Can't default to a+b, default to NaN
p.parse( varargin{:} );
a = p.Results.a;
b = p.Results.b;
c = p.Results.c;
if isnan(c) % Handle the defaulted case
c = a + b;
end
end
This would get used like myFunc( 'b', 4 );. This approach is also agnostic to the input order because of the name-value pairs, so you can also do something like myFunc( 'c', 3, 'a', 1 );

Convert cell arrays into single array

Getting started with Matlab and mySQL, as a result for my query I get this type of array:
my_result =
5×1 cell array
{'a'}
{'b'}
{'c'}
{'d'}
{'e'}
I'd like to get a simple array like this:
[a, b, c, d, e]
Here's my code:
mysql( 'open', 'my_database', 'usr','passw' )
query = fileread('query1.sql');
query = sprintf(query)
my_result = mysql(query);
And my attempts at getting a simple array:
my_array = []
for i=1:length(my_result)
my_array = [my_array, my_result{i}];
end
>> my_array
my_array =
'abcde'
>> cell2mat(my_result)
Error using cat
Dimensions of arrays being concatenated are not consistent.
Error in cell2mat (line 83)
m{n} = cat(1,c{:,n});
Is there a way to either get the correct format in the first place, or easily convert it properly? Thank you
just use the cell2mat built-in function
a{1} = 'a'
a{2} = 'b'
a{3} = 'c'
a{4} = 'd'
a{5} = 'e'
a =
5×1 cell array
{'a'}
{'b'}
{'c'}
{'d'}
{'e'}
cell2mat(a)
ans =
5×1 char array
'a'
'b'
'c'
'd'
'e'

Passing a table as argument to function in Lua

I want to loop through different indexed tables by only passing the initial table as an argument.
I currently have this table:
local table = {
stuff_1 = {
categories = {},
[1] = {
name = 'wui',
time = 300
}
},
stuff_2 = {
categories = {'stuff_10', 'stuff_11', 'stuff_12'},
stuff_10 = {
categories = {},
[1] = {
name = 'peo',
time = 150
},
[2] = {
name = 'uik',
time = 15
},
[3] = {
name = 'kpk',
time = 1230
},
[4] = {
name = 'aer',
time = 5000
}
},
stuff_11 = {
categories = {},
[1] = {
name = 'juio',
time = 600
}
},
stuff_12 = {
categories = {},
[1] = {
name = 'erq',
time = 980
},
[2] = {
name = 'faf',
time = 8170
}
}
}
I wanted to make a recursive function to check if the name in any of those tables was equal to some certain thing and return a string.
The recursivity lies in the idea of updating this table with whatever ammount I'd like (or until a certain limit).
I don't understand exactly what's wrong since when I try:
for k, v in pairs(table) do
print(k, v, #v.categories)
end
It correctly prints:
stuff_2 table: 0x10abb0 3
stuff_1 table: 0x10aab8 0
But when passing the table as a parameter to the the function below, it gives this error:
[string "stdin"]:84: attempt to get length of field 'categories' (a nil value)
Function:
function checkMessage(table)
local i = 1
local message = ""
for k, v in pairs(table) do
if(#v.categories == 0) then
while(v[i]) do
if(v[i].name == 'opd') then
if(v[i].time ~= 0) then
message = "return_1"
else
message = "return_2"
end
end
i = i + 1
end
else
checkMessage(table[k])
end
end
return message
end
EDIT: The problem lies in not ignoring that when using pairs onto the table, this doesn't just have tables with a category subtable but it also has a table named category, if this is ignored then the problem is fixed.
You're recursing into subtables that don't have a categories field. Trying to access categories on them yields nil, which you then try to use the length operator on. Hence your error:
attempt to get length of field 'categories' (a nil value)
If you can't hand trace your app, put in more print statements or get a line level debugger.