Lua sorting values of tables within table - sorting

I am very new to Lua, so please be gentle.
I want a sorted results based on the "error" key. For this example, the output should be:
c 50 70
d 25 50
b 30 40
a 10 20
Here is my script:
records = {}
records["a"] = {["count"] = 10, ["error"] = 20}
records["b"] = {["count"] = 30, ["error"] = 40}
records["c"] = {["count"] = 50, ["error"] = 70}
records["d"] = {["count"] = 25, ["error"] = 50}
function spairs(t, order)
-- collect the keys
local keys = {}
for k in pairs(t) do keys[#keys+1] = k end
-- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys
if order then
table.sort(keys, function(a,b) return order(t, a, b) end)
else
table.sort(keys)
end
-- return the iterator function
local i = 0
return function()
i = i + 1
if keys[i] then
return keys[i], t[keys[i]]
end
end
end
for k, v in pairs(records) do
for m, n in pairs(v) do
for x, y in spairs(v, function(t,a,b) return t[b] < t[a] end) do
line = string.format("%s %5s %-10d", k, n, y)
end
end
print(line)
end
I found this about sorting a table and tried to implement it. But it does not work, results are not sorted.

table.sort only works when the table elements are integrally indexed. In your case; when you try to call spairs, you are actually calling table.sort on the count and error indices.
First off; remove the ugly, irrelevant nested for..pairs loops. You only need the spairs for your task.
for x, y in spairs(records, function(t, a, b) return t[b].error < t[a].error end) do
print( x, y.count, y.error)
end
And that is all.

Related

Function to find X numbers that add up to a certain value

I need a function that finds a variable amount of numbers, which together must add up to a certain value. In this case it is 8.
The numbers which can be added together are predefined in a table, to make things easier.
Current approach: Shuffle the table using a small algorithm, add first X values together, if they don't add up to 8, start over (including shuffling again) until the first X values add up to 8.
My code does work, just 2 problems: It takes a long time to process (obviously) and it can cause a stack overflow error if I don't add a cooldown.
Code can be dirty, it's not for a live production. Also im only an intermediate lua developer at best...
function sleep (a) -- random sleep function I found
local sec = tonumber(os.clock() + a);
while (os.clock() < sec) do
end
end
function shuffle(tbl) -- random shuffle function I found
for i = #tbl, 2, -1 do
math.randomseed( os.time() )
math.random();math.random();math.random();math.random();
local j = math.random(i)
tbl[i], tbl[j] = tbl[j], tbl[i]
end
return tbl
end
local times = {
0.5,
1.0,
1.5,
2.0,
2.5,
3.0,
3.5,
4.0
}
local timeunits = {} --refer to line 49, I did not want to do it like that...
function nnumbersto8(amount)
local sum = 0
local numbs = {}
times = shuffle(times) --reshuffle the set
for i = 1,amount,1 do --add first x values together
sum = sum + times[i]
numbs[i] = times[i]
end
if sum ~= 8 then sleep(0.1) nnumbersto8(amount) return end --if they are not 8, repeat process with cooldown to avoid stack overflow
--return numbs -- This doesn't work for some reason, nothing gets returned outside the function
timeunits = numbs
end
nnumbersto8(5) -- manual run it for now
print(unpack(timeunits))
There must be a simpler way, right?
Thanks in advance, any help is appreciated!
Here is a method that will work for large numbers of elements, and will pick a random solution with theoretically even likelihood for each.
function solution_node (value, count, remainder)
local node = {}
node.value = value
node.count = count
node.remainder = remainder
return node
end
function choose_solutions (node1, node2)
if node1 == nil then
return node2
elseif node2 == nil then
return node1
else
-- Make a random choice of which solution to pick.
if node1.count < math.random(node1.count + node2.count) then
node2.count = node1.count + node2.count
return node2
else
node1.count = node1.count + node2.count
return node1
end
end
end
function decode_solution (node)
if node == nil then
return nil
end
answer = {}
while node.value ~= nil do
table.insert(answer, node.value)
-- This causes the solution to be randomly shuffled.
local i = math.random(#answer)
answer[#answer], answer[i] = answer[i], answer[#answer]
node = node.remainder
end
return answer
end
function random_sum(tbl, count, target)
local choices = {}
-- Normally arrays are not 0-based in Lua but this is very convenient.
for j = 0,count do
choices[j] = {}
end
-- Make sure that the empty set is there.
choices[0][0.0] = solution_node(nil, 1, nil)
for i = 1,#tbl do
for j = count,1,-1 do
for this_sum, node in pairs(choices[j-1]) do
local next_sum = this_sum + tbl[i]
local next_node = solution_node(tbl[i], node.count, node)
-- Try adding this value in to a solution.
if next_sum <= target then
choices[j][next_sum] = choose_solutions(next_node, choices[j][next_sum])
end
end
end
end
return decode_solution(choices[count][target])
end
local times = {
0.2,
0.3,
0.5,
1.0,
1.2,
1.3,
1.5,
2.0,
2.5,
3.0,
3.5,
4.0
}
math.randomseed( os.time() )
local result = random_sum(times, 5, 8.0)
print("answer")
for k, v in pairs(result) do print(v) end
Sorry for my code. I haven't coded in Lua for a few years.
This is the subset sum problem with an extra restriction on the number of elements you are allowed to choose.
The solution is to use Dynamic Programming similar to regular Subset Sum, but add an extra variable that indicates how many items you have used.
This should go something among the lines of:
Failing stop clauses:
DP[-1][x][n] = false, for all x,n>0 // out of elements
DP[i][-1][n] = false, for all i,n>0 // exceeded X items
DP[i][x][n] = false n < 0 // Passed the sum limit. This is an optimization only if all elements are non negative.
Successful stop clause:
DP[i][0][0] = true for all i >= 0
Recursive formula:
DP[i][x][n] = DP[i-1][x][n] OR DP[i-1][x-1][n-item[i]] // Watch for n<item[i] case here.
^ ^
Did not take the item Used the item
There are no solutions for 1, 2 and for values greater than 5, so the function only accepts 3, 4 and 5.
Here we are doing a shallow copy of the times table then we get a random index from the copy and begin searching for the solution, removing values we use as we go.
local times = {
0.5,
1.0,
1.5,
2.0,
2.5,
3.0,
3.5,
4.0
}
function nNumbersTo8(amount)
if amount < 3 or amount > 5 then
return {}
end
local sum = 0
local numbers = {}
local set = {table.unpack(times)}
for i = 1, amount - 1, 1 do
local index = math.random(#set)
local value = set[index]
if not (8 < (sum + value)) then
sum = sum + value
table.insert(numbers, value)
table.remove(set, index)
else
break
end
end
local reminder = 8 - sum
for _,v in ipairs(set)do
if v == reminder then
sum = sum + v
table.insert(numbers, v)
break
end
end
if #numbers == amount then
return numbers
else
return nNumbersTo8(amount)
end
end
for i=1,100 do
print(table.unpack(nNumbersTo8(5)))
end
Example response:
1.5 0.5 3 2 1
3 0.5 1.5 1 2
2 3 1.5 0.5 1
3 2 1.5 1 0.5
0.5 1 2 3 1.5

Lua iterate over table sorted by values

I have a table t with many entries like t["name1"] = 42, t["name2"] = 123, ...
I would like to iterate over the table in descending order of the value numbers. How can this be accomplished? I have found methods to create iterator functions that go ordered over the keys of a table, but no way to go over the entries with ordered values.
function pairs_order_by_values_desc(tab)
local keys = {}
for k in pairs(tab) do
keys[#keys + 1] = k
end
table.sort(keys, function(a, b) return tab[a] > tab[b] end)
local j = 0
return
function()
j = j + 1
local k = keys[j]
if k ~= nil then
return k, tab[k]
end
end
end
local t = {}
t.name1 = 42
t.name2 = 123
t.name3 = 99
for k, v in pairs_order_by_values_desc(t) do
print(k, v)
end

Returning A Sorted List's Index in Lua

I access object properties with an index number
object = {}
object.y = {60,20,40}
object.g = {box1,box2,box3} -- graphic
object.c = {false,false,false} -- collision
-- object.y[2] is 20 and its graphic is box2
-- sorted by y location, index should be, object.sort = {2,3,1}
I know table.sort sorts a list, but how can I sort the y list that returns index for the purpose of drawing each object in-front depending on the y location.
Maybe the quicksort function can be edited, I don't understand it.
http://rosettacode.org/wiki/Sorting_algorithms/Quicksort#Lua
https://github.com/mirven/lua_snippets/blob/master/lua/quicksort.lua
Is this possible?
Do not store the data as you're currently doing. Use something like:
object = {
{
y = 60,
g = box1,
c = false,
},
{
y = 20,
g = box2,
c = false,
},
{
y = 40,
g = box3,
c = false,
},
}
and then use the following callback function in table.sort:
function CustomSort(L, R)
return L.y > R.y
end
as shown below:
table.sort(object, CustomSort)
This should work:
local temp = {}
local values = object.y
-- filling temp with all indexes
for i=1,#values do
temp[i] = i
end
-- sorting the indexes, using object.y as comparison
table.sort(temp,function(a,b)
return values[a] < values[b]
end)
-- sorting is done here, have fun with it
object.sort = temp
temp will be {2,3,1} when using this code combined with yours.
#EinsteinK #hjpotter92 : Thank you
RESULT: This is the final version of the answers I received. My question is solved.
Use sortIndex(object) to get sorted list in object.sort . Update sort after objects move.
box1 = love.graphics.newImage("tile1.png")
box2 = love.graphics.newImage("tile2.png")
box3 = love.graphics.newImage("tile3.png")
hero = love.graphics.newImage("hero.png")
object = {
{ x = 200, y = 50, g = box1 },
{ x = 50, y = 100, g = box2 },
{ x = 150, y = 200, g = box3 },
{ x = 0, y = 0, g = hero }
}
function sortIndex(item)
-- Sort id, using item values
local function sortY(a,b)
return item[a].y < item[b].y
end
--------------------------------
local i
local id = {} -- id list
for i = 1, #item do -- Fill id list
id[i] = i
end
-- print( unpack(id) ) -- Check before
table.sort(id,sortY)-- Sort list
-- print( unpack(id) ) -- Check after
item.sort = id -- List added to object.sort
end
sortIndex(object) -- print( unpack(object.sort) ) -- Check sorted id's
function drawObject()
local i,v, g,x,y
for i = 1, #object do
v = object.sort[i] -- Draw in order
x = object[v].x
y = object[v].y
g = object[v].g
love.graphics.draw(g,x,y)
end
end

randoming a table Corona SDK / Lua

Could anyone help me with a way to randomly fill a table with N values, where the values are 1,...,M allowing no duplicates ?
Cheers.
local M, N, tNonFinal, tFinal = 500, 20, {}, {}
math.randomseed( os.time() )
for i = 1, N, 1 do
local iRandom = math.random(1, M)
while tNonFinal[iRandom] do
iRandom = math.random(1, M)
end
table.insert( tNonFinal, iRandom, true )
tFinal[i] = iRandom
end
Your required table will be tFinal. You can also add a condition where if M < N then N = M end
This may help you...
local myArray = {}
local valueArray = {1,2,3,4,5,6,7,8,9,10} -- let it be the array with values 1,2...M
local index = 0
local isFetched = {}
for i=1,#valueArray do
isFetched[i] = 0
end
local randomValue = 0
local function addTomyArray()
randomValue = math.random(#valueArray)
if(isFetched[randomValue]==0)then
index = index + 1
isFetched[randomValue] = 1
myArray[index] = valueArray[randomValue]
if(index==#valueArray)then
for i=1,#myArray do
print(myArray[i]) -- result : shuffled array
end
end
else
addTomyArray()
end
end
timer.performWithDelay(0,addTomyArray,#valueArray) -- #valueArray
Keep coding........ 😃

copying unordered keys from one table to ordered values in another

I have a table mapping strings to numbers like this:
t['a']=10
t['b']=2
t['c']=4
t['d']=11
From this I want to create an array-like table whose values are the keys from the first table, ordered by their (descending) values in the first table, like this:
T[1] = 'd' -- 11
T[2] = 'a' -- 10
T[3] = 'c' -- 4
T[4] = 'b' -- 2
How can this be done in Lua?
-- Your table
local t = { }
t["a"] = 10
t["b"] = 2
t["c"] = 4
t["d"] = 11
local T = { } -- Result goes here
-- Store both key and value as pairs
for k, v in pairs(t) do
T[#T + 1] = { k = k, v = v }
end
-- Sort by value
table.sort(T, function(lhs, rhs) return lhs.v > rhs.v end)
-- Leave only keys, drop values
for i = 1, #T do
T[i] = T[i].k
end
-- Print the result
for i = 1, #T do
print("T["..i.."] = " .. ("%q"):format(T[i]))
end
It prints
T[1] = "d"
T[2] = "a"
T[3] = "c"
T[4] = "b"
Alexander Gladysh's answer can be simplified slightly:
-- Your table
local t = { }
t["a"] = 10
t["b"] = 2
t["c"] = 4
t["d"] = 11
local T = { } -- Result goes here
-- Store keys (in arbitrary order) in the output table
for k, _ in pairs(t) do
T[#T + 1] = k
end
-- Sort by value
table.sort(T, function(lhs, rhs) return t[lhs] > t[rhs] end)
-- Print the result
for i = 1, #T do
print("T["..i.."] = " .. ("%q"):format(T[i]))
end
Tables in Lua do not have an order associated with them.
When using a table as an array with sequential integer keys from 1 to N, the table can be iterated in order using a loop or ipairs().
When using keys that are not sequential integers from 1 to N, the order can not be controlled. To get around this limitation a second table can be used as an array to store the order of the keys in the first table.

Resources