I have a table with two values, one is a name (string and unique) and the other is a number value (in this case hearts). What I want is this: sort the table by hearts but scramble randomly the items when there is a tie (e.g. hearts is equal). By a standard sorting function, in case of ties the order is always the same and I need it to be different every time the sorting function works. This is anexample:
tbl = {{name = "a", hearts = 5}, {name = "b", hearts = 2}, {name = "c", hearts = 6}, {name = "d", hearts = 2}, {name = "e", hearts = 2}, {name = "f", hearts = 7}}
sort1 = function (a, b) return a.hearts > b.hearts end
sort2 = function (a, b)
if a.hearts ~= b.hearts then return a.hearts > b.hearts
else return a.name > b.name end
end
table.sort(tbl, sort2)
local s = ""
for i = 1, #tbl do
s = s .. tbl[i].name .. "(" .. tbl[i].hearts .. ") "
end
print(s)
Now, with the function sort2
I think I quite got the problem. The problem is, what happens when a.hearts == b.hearts
? In my code it just orders the ties by their name, not what I want. I have two ideas:
sort1
.rnd
, that is a random number. Then in sort2
, when a.hearts == b.hearts
order the items by a.rnd > b.rnd
.sort2
, when a.hearts == b.hearts
generate randomly true or false and return it. It doesn't work, and I understand that this happens because the random true/false makes the order function crash since there could be inconsistencies.I don't like 1 (because I would like to do everything inside the sorting function) and 2 (since it requires to add a value), I would like to do something like 3 but working. The question is: is there a way do to this in a simple manner, and what is an optimal way of doing this? (maybe, method 1 or 2 are optimal and I don't get it).
Bonus question. Moreover, I need to fix an item and sort the others. For example, suppose we want "c"
to be first. Is it good to make a separate table with only the items to sort, sort the table and then add the fixed items?
-- example table
local tbl = {
{ name = "a", hearts = 5 },
{ name = "b", hearts = 2 },
{ name = "c", hearts = 6 },
{ name = "d", hearts = 2 },
{ name = "e", hearts = 2 },
{ name = "f", hearts = 7 },
}
-- avoid same results on subsequent requests
math.randomseed( os.time() )
---
-- Randomly sort a table
--
-- @param tbl Table to be sorted
-- @param corrections Table with your corrections
--
function rnd_sort( tbl, corrections )
local rnd = corrections or {}
table.sort( tbl,
function ( a, b)
rnd[a.name] = rnd[a.name] or math.random()
rnd[b.name] = rnd[b.name] or math.random()
return a.hearts + rnd[a.name] > b.hearts + rnd[b.name]
end )
end
---
-- Show the values of our table for debug purposes
--
function show( tbl )
local s = ""
for i = 1, #tbl do
s = s .. tbl[i].name .. "(" .. tbl[i].hearts .. ") "
end
print(s)
end
for i = 1, 10 do
rnd_sort(tbl)
show(tbl)
end
rnd_sort( tbl, {c=1000000} ) -- now "c" will be the first
show(tbl)