Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lua - Sort table and randomize ties

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:

  1. First scramble randomly all the items in the table, then apply sort1.
  2. Add a value to every element of the table, called rnd, that is a random number. Then in sort2, when a.hearts == b.hearts order the items by a.rnd > b.rnd.
  3. In 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?

like image 493
Matteo Silvestro Avatar asked Jul 22 '26 06:07

Matteo Silvestro


1 Answers

-- 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)
like image 198
Egor Skriptunoff Avatar answered Jul 23 '26 19:07

Egor Skriptunoff



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!