Lua - Table in a Table Printing and Sorting - lua

T = {
{Name = "Mark", HP = 54, Breed = "Ghost"},
{Name = "Stan", HP = 24, Breed = "Zombie"},
{Name = "Juli", HP = 100, Breed = "Human"}},
Questions:
How would I Print just the names?
and
How can I sort it by HP?

You need to iterate over the table by using either the pairs or ipairs function to print the name. ipairs iterates from 1 to N (numeric indices only), while pairs iterates over every element, in no defined order.
> T = { {Name = "Mark", HP = 54, Breed = "Ghost"}, {Name = "Stan", HP = 24, Breed = "Zombie"}, {Name = "Juli", HP = 100, Breed = "Human"}}
> for _,t in ipairs(T) do print(t.Name) end
Mark
Stan
Juli
Then you can use the table.sort function to sort the table in-place:
> table.sort(T, function(x,y) return x.HP < y.HP end)
> for _,t in ipairs(T) do print(t.Name, t.HP) end
Stan 24
Mark 54
Juli 100
The second argument to table.sort is a comparison function of your choice; in this case, we only wanted to compare the HP values.

Related

Get all repeated itens from a table in Lua

I need a little help here
Let's suppose I have a table with numbers.
tbl = {'item1' = 6, 'item2' = 1, 'item3' = 6, 'item4' = 3, 'item5' = 2, 'item5' = 3}
I wanna put all repeated numbers in the same table (with key and value) like this:
repeated = {'item1' = 6, 'item3' = 6, 'item4' = 3, 'item5' = 3}
and creat a new one with the "not repeated" numbers:
notrepeated = {'item2' = 1, 'item5' = 2}
Can someone help? Thank you so much.
-- Count the items for each number
local itemsByNum = {}
for item, num in pairs(tbl) do
itemsByNum[num] = (itemsByNum[num] or 0) + 1
end
-- Now move objects to the respective tables
local rep, noRep = {}, {} -- can't use "repeat" as that's a Lua keyword
for item, num in pairs(tbl) do
if itemsByNum[num] > 1 then -- repeated at least once
rep[item] = num
else -- unique number
norep[item] = num
end
end

How does one reverse the items in a table in Lua?

Is there something that would do the same thing as table.sort, but backwards?
The table.sort function allows you to define a sorting function as the second argument. It returns a boolean and specifies the order on which the table must be ordered:
local atable = {1,2,3}
table.sort(atable, function(a,b) return a > b end)
for _, v in pairs(atable) do
print(v)
end
--[[This prints:
3
2
1
]]
Of course, this doesn't limit itself to "lists" but you can use them on other table types as well:
local cityInfo = {
{name = "Vancouver", population = 321, location = "Canada"},
{name = "Paris", population = 123, location = "France"},
{name = "London", population = 1000, location = "United Kingdom"},
}
table.sort(cityInfo, function(a,b) return a.population > b.population end) -- Here the items are ordered in descencing order based on the population field
for _, v in pairs(cityInfo) do
print(v.population)
end
--[[This prints:
1000
321
123
]]
If you want to sort a table in reverse order:
table.sort(your_table, function(x, y) return x > y end)
If you want to reverse a table:
local function reverse(tab)
for i = 1, #tab//2, 1 do
tab[i], tab[#tab-i+1] = tab[#tab-i+1], tab[i]
end
return tab
end

add value afterwards to key in table lua

Does someone know how to add an value to an key which already has an value ?
for example:
x = {}
x[1] = {string = "hallo"}
x[1] = {number = 10}
print(x[1].string) --nil
print(x[1].number) --10
It should be possible to print both things out. The same way how it is here possible:
x[1] = { string = "hallo" ; number = 10}
I just need to add some informations afterwards to the table and especially to the same key.
Thanks!
x = {} -- create an empty table
x[1] = {string = "hallo"} -- assign a table with 1 element to x[1]
x[1] = {number = 10} -- assign another table to x[1]
The second assignment overwrites the first assignment.
x[1]["number"] = 10 or short x[1].number = 10 will add a field number with value 10 to the table x[1]
Notice that your x[1] = { string = "hallo" ; number = 10} is acutally equivalent to
x[1] = {}
x[1]["string"] = "hallo"
x[1]["number"] = 10

Lua Table - Search for Items that starts with an Letter

i have this table
animals = {
{sname = "bunny", name = "bunny hase", size = 4, size2 = 8, size3 = 9},
{sname = "mouse", name = "Micky Mouse", size = 1, size2 = 12, size3 = 22},
{sname = "cow", name = "Die Kuh", size = 30, size2 = 33, size3 = 324
}
there i can search by a listed entry
for _,v in pairs(animals) do
if v.sname == "bunny" then
print(v.sname, v.name, v.size, v.size2, v.size3)
break
end
end
and get the result:
bunny bunny hase 4 8 9
Now i want to search in my table by starting with a single Letter, for example "b", that show me all the entries starting with the letter "b" to get the same result?
I found no Solution. May you can help me?
First: The table animals needs a trailing } ;-)
Put it in a Lua -i console and play around with...
>animals = {
{sname = "bunny", name = "bunny hase", size = 4, size2 = 8, size3 = 9},
{sname = "mouse", name = "Micky Mouse", size = 1, size2 = 12, size3 = 22},
{sname = "cow", name = "Die Kuh", size = 30, size2 = 33, size3 = 324}
}
-- Now set a __call metamethod on same table
>setmetatable(animals,{__call=function(tab,...)
local args={...}
for key, value in pairs(tab) do
if value.sname:find(args[1],1) then print(key,'=',value.sname) end
end
end})
table: 0x565c4a00
-- Lets try it once
>animals('b')
1 = bunny
-- Next one
>animals('c')
3 = cow
-- Last one
>animals('m')
2 = mouse
Using metatables holds your stuff together.
Another fine place is the __index metamethod that can hold all functions you need for that table and can be used like the string functions on a string.
( Like: value.sname:find(args[1],1) )
This leads to the heart of what find should do.
In first example it looks in whole sname for a matching pattern.
Check the Lua patterns what also can be useful.
Maybe a ^ only for the begining sounds smart?
So construct the find pattern: '^'..args[1]
...and use more than one letter if you have a cow, crow, frog and fish in your animals.
Example with function name find in __index
>animals = {
{sname = "bunny", name = "bunny hase", size = 4, size2 = 8, size3 = 9},
{sname = "mouse", name = "Micky Mouse", size = 1, size2 = 12, size3 = 22},
{sname = "cow", name = "Die Kuh", size = 30, size2 = 33, size3 = 324}
}
-- Place a find function into __index
>setmetatable(animals,{__index={find=function(tab,...)
local args={...}
for key, value in pairs(tab) do
if value.sname:find('^'..args[1]) then print(key,'=',value.sname) end
end
end}})
table: 0x565c3db0
-- first
>animals:find('c')
3 = cow
-- next
>animals:find('m')
2 = mouse
-- last
>animals:find('b')
1 = bunny
If you like to print all key values then extend the print() in find().
Stop, i found an issue....
Look here - i prefer the first solution:
animals = {
{sname = "bunny", name = "bunny hase", size = 4, size2 = 8, size3 = 9},
{sname = "mouse", name = "Micky Mouse", size = 1, size2 = 12, size3 = 22},
{sname = "cow", name = "Die Kuh", size = 30, size2 = 33, size3 = 324}
}
-- Now set a __call metamethod on same table
setmetatable(animals,{__call=function(tab,...)
local args={...}
for v,k in pairs(tab) do
if k.sname:find(args[1],1) then print(v,'=',k.sname) end
end
end})
-- Search Entries with Start U.....
-- there should be no result, but....
animals('u')
i get the Result:
1 = bunny
2 = mouse
that should not be the result!

F# - Flatten List/Range

I'm new to F# and am wondering how I would go about flattening a list.
Essentially in the database I store a record with a min_age and max_age range (this is a fictitious example for the sake of brevity - i am not agist!). My fields look something like the following:
id,
cost,
savings,
min_age,
max_age
I essentially have an F# class that acts as a one-to-one mapping with this table - i.e. all properties are mapped exactly to the database fields.
What I would like to do is flatten this range. So, instead of a list containing items like this:
saving_id = 1, cost = 100, savings = 20, min_age = 20, max_age = 26
saving_id = 2, cost = 110, savings = 10, min_age = 27, max_age = 31
I would like a list containing items like this:
saving_id = 1, cost = 100, savings = 20, age = 20
saving_id = 1, cost = 100, savings = 20, age = 21
etc.
saving_id = 2, cost = 110, savings = 10, age = 27
saving_id = 2, cost = 110, savings = 10, age = 28
etc.
Is there any in-built mechanism to flatten a list in this manner and/or does anyone know how to achieve this?
Thanks in advance,
JP
You might want to use Seq.collect. It concatenates sequences together, so in your case, you can map a function over your input that splits a single age range record to a sequence of age records and use Seq.collect to glue them together.
For example:
type myRecord =
{ saving_id: int;
cost: int;
savings: int;
min_age: int;
max_age: int }
type resultRecord =
{ saving_id: int;
cost: int;
savings: int;
age: int }
let records =
[ { saving_id = 1; cost = 100; savings = 20; min_age = 20; max_age = 26 }
{ saving_id = 2; cost = 110; savings = 10; min_age = 27; max_age = 31 } ]
let splitRecord (r:myRecord) =
seq { for ageCounter in r.min_age .. r.max_age ->
{ saving_id = r.saving_id;
cost = r.cost;
savings = r.savings;
age = ageCounter }
}
let ageRanges = records |> Seq.collect splitRecord
Edit: you can also use a sequence generator with yield!
let thisAlsoWorks =
seq { for r in records do yield! splitRecord r }
Agreeing with cfern's answer, but was wondering if this might benefit from seeing another "built-in" function used. Here's an alternative version of the splitRecord function that shows the library call for unfolding a sequence. No gain here other than having an example for Seq.unfold.
let splitRecord (r:myRecord) =
Seq.unfold (fun curr_age ->
if curr_age <= r.max_age then
Some({ saving_id = r.saving_id;
cost = r.cost;
savings = r.savings;
age = curr_age } ,
curr_age + 1)
else None)
r.min_age

Resources