sortinglualua-table

Lua table.sort invokes the comparing function with the same element


I have stumbled upon a strange behavior of table.sort(), in which the comparing function is invoked by passing the same array element in both parameters.

Consider the code below. I want to sort the array from highest to lowest value of the property v and, in case they are equal (and since table.sort() algorithm is not stable as of the docs), I want to sort them by the original indices of the elements.

-- The array to sort
local arr = {
  {id="A", v = 1},
  {id="B", v = 1},
  {id="C", v = 0},
  {id="D", v = 1},
  {id="E", v = 1}
}

-- A map containing the original index of the elements in the array: element => originalIndex
local original_indices = {}
for index, elem in ipairs(arr) do
  original_indices[elem] = index
end

-- Sort the array
table.sort(arr, 
  function(a, b) 
    assert(a ~= b, "Comparing the same element in the array!")
    
    -- Compare the value
    if a.v > b.v then
      return true
    elseif a.v < b.v then
      return false
    end
    
    -- Values are equal. Compare original indices, which should always
    -- decide the final order.
    local ia = original_indices[a]
    local ib = original_indices[b]
    
    if ia < ib then
      return true
    elseif ia > ib then
      return false
    end
    
    error("BUG! Comparing the same element in the array!") 
  end
)

The expected result should be:

{  
  {id="A", v = 1},
  {id="B", v = 1},
  {id="D", v = 1},
  {id="E", v = 1},
  {id="C", v = 0}
}

But, instead, I get an error because Lua is invoking the sorting function by passing the same element in both parameters, which should not.

Am I missing something? How can I get the expected result?

You can play around with the code here.


Solution

  • To solve your problem, simply return when comparing the same element.

    if a == b then
      return
    end
    

    Internally the sorting algorithm of Lua is the quick sort of Hoare partition scheme. During partitioning, the pivot value will compare each element one by one from each side until it finds the element with the same or opposite value, so there is a chance that the same element is compared.