Lua Sort Table by Two Values? - sorting

So I have the following table:
servers = {"ProtectedMethod" = {name = "ProtectedMethod", visits = 20, players = 2}, "InjecTive" = {name = "InjecTive", visits = 33, players = 1}};
How would I sort the sub-tables in the servers table, into a new array based on players first, and then number of visits, meaning that you don't sort by visits unless two tables have the same value for players.
For example if the sorting code was put into a function called tableSort, I should be able to call the following code:
sorted = sort();
print(sorted[1].name .. ": " sorted[1].players .. ", " .. sorted[1].visits); --Should print "ProtectedMethod: 2, 20"
print(sorted[2].name .. ": " sorted[2].players .. ", " .. sorted[2].visits); --Should print "InjecTive: 1, 33"
TIA

You have a hash, so you need to convert it to an array and then sort:
function mysort(s)
-- convert hash to array
local t = {}
for k, v in pairs(s) do
table.insert(t, v)
end
-- sort
table.sort(t, function(a, b)
if a.players ~= b.players then
return a.players > b.players
end
return a.visits > b.visits
end)
return t
end
servers = {
ProtectedMethod = {
name = "ProtectedMethod", visits = 20, players = 2
},
InjecTive = {
name = "InjecTive", visits = 33, players = 1
}
}
local sorted = mysort(servers)
print(sorted[1].name .. ": " .. sorted[1].players .. ", " .. sorted[1].visits)
print(sorted[2].name .. ": " .. sorted[2].players .. ", " .. sorted[2].visits)

Related

Lua sorting values of tables within table

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.

Getting table values once the for-do loop is over

I have coded the following function, to draw a grid on the screen (using FlyWithLua in X-Plane 10).
It works and paints the grid as it should, boxes/labels and everything, and obviously it creates the "lt_box" three dimensional array as when I print in the loop (the commented out print instructions) it prints the correct values.
The issue is, if I call lt_box[x][y][z] from any other part of the code I can't get to those values...
any idea?
function draw_lt_table()
idx = 1
idx2 = 1
xx1 = rth_box[1]
xx2 = rth_box[2]
yy1 = rth_box[3]
yy2 = rth_box[4]
lite_idx = 1
cell_x_size = 38
cell_y_size = 40
num_cols = 6
num_rows = 6
lt_box = {}
if d_tab==false or d_stg==false then
return lt_box
end
for idx2 = 1,num_rows,1 do
for idx = 1,num_cols,1 do
lt_box[idx] = {}
lt_box[idx][idx2] = {xx1+((cell_x_size*idx)-cell_x_size), xx1+(cell_x_size)*idx, 1+yy2+((cell_y_size*idx2)-cell_y_size), 1+yy2+(cell_y_size)*idx2}
graphics.set_color(0, 0, 0, 0.7)
graphics.draw_rectangle(lt_box[idx][idx2][1], lt_box[idx][idx2][3], lt_box[idx][idx2][2], lt_box[idx][idx2][4])
graphics.set_color(1, 1, 1, 0.7)
graphics.set_width(2)
graphics.draw_line(lt_box[idx][idx2][1],lt_box[idx][idx2][3],lt_box[idx][idx2][1],lt_box[idx][idx2][4])
graphics.draw_line(lt_box[idx][idx2][1],lt_box[idx][idx2][4],lt_box[idx][idx2][2],lt_box[idx][idx2][4])
graphics.draw_line(lt_box[idx][idx2][2],lt_box[idx][idx2][4],lt_box[idx][idx2][2],lt_box[idx][idx2][3])
graphics.draw_line(lt_box[idx][idx2][2],lt_box[idx][idx2][3],lt_box[idx][idx2][1],lt_box[idx][idx2][3])
light_up(unpack(lt_box[idx][idx2]))
glColor4f(1, 1, 1, 0.7)
draw_string(lt_box[idx][idx2][1]+12, lt_box[idx][idx2][3]+30, lite_idx)
draw_string(lt_box[idx][idx2][1]+3, lt_box[idx][idx2][3]+15, lites_vars[4][lite_idx])
draw_string(lt_box[idx][idx2][1]+3, lt_box[idx][idx2][3]+5, lites_vars[lt_set][lite_idx])
lite_idx = lite_idx+1 -- this is usd in line 115-116 to select values as labels
--[[
print("LT_BOX " .. idx .. " " .. idx2 .. " x1 ".. lt_box[idx][idx2][1])
print("LT_BOX " .. idx .. " " .. idx2 .. " x2 ".. lt_box[idx][idx2][2])
print("LT_BOX " .. idx .. " " .. idx2 .. " y1 ".. lt_box[idx][idx2][3])
print("LT_BOX " .. idx .. " " .. idx2 .. " y2 ".. lt_box[idx][idx2][4])
print("****************************************************************************")
--]]
end
--print("____________________________________________________________________________")
end
end
You have lt_box[idx] = {} in the inner loop, which means you reset that table every time you add an element. You have to create that table in the outer loop.
for idx = 1,num_cols,1 do
lt_box[idx] = {}
for idx2 = 1,num_rows,1 do

Sorting a table by nested value in Lua [duplicate]

This question already has answers here:
Associatively sorting a table by value in Lua
(7 answers)
Closed 8 years ago.
I have a program which aggregates for every user the total number of downloads performed with an aggregate of the total downloaded data in kb.
local table = {}
table[userID] = {5, 23498502}
My aim is that the output of the printTable function will produce the entire list of users ordered in descending order by the amount of kb downloaded v[2]
local aUsers = {}
...
function topUsers(key, nDownloads, totalSize)
if aUsers[key] then
aUsers[key][1] = aUsers[key][1] + nDownloads
aUsers[key][2] = aUsers[key][2] + totalSize
else
aUsers[key] = {nDownloads, totalSize}
end
end
function printTable(t)
local str = ""
-- How to sort 't' so that it prints in v[2] descending order?
for k,v in pairs(t) do
str = str .. k .. ", " .. v[1] .. ", " .. v[2] .. "\n"
end
return str
end
...
Any ideas how could I do that?
You can get the keys into a separate table and then sort that table using the criteria you need:
local t = {
a = {1,2},
b = {2,3},
c = {4,1},
d = {9,9},
}
local keys = {}
for k in pairs(t) do table.insert(keys, k) end
table.sort(keys, function(a, b) return t[a][2] > t[b][2] end)
for _, k in ipairs(keys) do print(k, t[k][1], t[k][2]) end
will print:
d 9 9
b 2 3
a 1 2
c 4 1

Groupby in MongoTemplate returning empty fields

I have a collection with fields id, a-int, b-int, c-int, total-int. I am trying to get a, b, c, total but I end up getting just the sum of total and rest of the field values are 0, 0, 0. How do I fix this? Expected result from the data sample below 10, 20, 30, 300
Thanks
Data sample
id, a, b, c, total
xid, 10, 20, 30, 100
xid, 10, 20, 30, 200
GroupBy groupBy = GroupBy.key("{a : 1, b : 1, c : 1}")
.initialDocument("{ total: 0 }")
.reduceFunction("function(obj, result) { result.total += obj.total; }");
GroupByResults<Grouped> results = mongoTemplate.group(Criteria.where("id").is(id),
TABLE, groupBy, Grouped.class);
I've got the result I think you wanted using the following:
GroupBy groupBy = GroupBy.key("a", "b", "c")
.initialDocument("{ total: 0 }")
.reduceFunction("function(obj, result) { " +
" result.a = obj.a; " +
" result.b = obj.b; " +
" result.c = obj.c; " +
" result.total += obj.total; " +
"}");
Note that what you need to do is tell the reduce function what to put into the a, b, and c fields as well as the total field.
This gave me a raw output of:
{ "a" : 10.0 , "b" : 20.0 , "c" : 30.0 , "total" : 300.0}
Since you haven't included the Grouped class, I'm not sure if this maps exactly into the object that you wanted, but it might point you in the right direction.

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