Не удается отсортировать таблицу по ассоциативным индексам
-
21-09-2019 - |
Вопрос
Почему я не могу использовать table.сортировать для сортировки таблиц с ассоциативными индексами?
Решение
Вы можете сортировать таблицы только по последовательным целочисленным ключам, начинающимся с 1, то есть по спискам.Если у вас есть другая таблица пар ключ-значение, вы можете составить список пар и отсортировать их:
function sortpairs(t, lt)
local u = { }
for k, v in pairs(t) do table.insert(u, { key = k, value = v }) end
table.sort(u, lt)
return u
end
Конечно, это полезно только в том случае, если вы предоставляете индивидуальный заказ (lt
) который ожидает в качестве аргументов пары ключ / значение.
Этот вопрос более подробно обсуждается в связанный вопрос о сортировке таблиц Lua.
Другие советы
В общем, таблицы Lua представляют собой чистые ассоциативные массивы.Не существует "естественного" порядка, отличного от побочного эффекта конкретной реализации хэш-таблицы, используемой в ядре Lua.Это имеет смысл, поскольку значения любого типа данных Lua (кроме nil
) может использоваться как в качестве ключей , так и в качестве значений;но только строки и числа имеют какой-либо разумный порядок, и то только между значениями подобного типа.
Например, каким должен быть порядок сортировки этой таблицы:
unsortable = {
answer=42,
true="Beauty",
[function() return 17 end] = function() return 42 end,
[math.pi] = "pi",
[ {} ] = {},
12, 11, 10, 9, 8
}
Он содержит один строковый ключ, один логический ключ, одну функциональную клавишу, один нецелочисленный ключ, один табличный ключ и пять целочисленных ключей.Должна ли функция сортировать перед строкой?Как вы сравниваете строку с числом?Где должна быть отсортирована таблица?И что насчет userdata
и thread
значения, которых случайно нет в этой таблице?
По соглашению, значения, индексированные последовательными целыми числами, начинающимися с 1, обычно используются в виде списков.Несколько функций и распространенных идиом следуют этому соглашению, и table.sort
это один из примеров.Функции, работающие со списками, обычно игнорируют любые значения, хранящиеся в ключах, которые не являются частью списка.Снова, table.sort
является примером:он сортирует только те элементы, которые хранятся по ключам, являющимся частью списка.
Другим примером является #
оператор.Для приведенной выше таблицы, #unsortable
равно 5, потому что unsortable[5] ~= nil
и unsortable[6] == nil
.Обратите внимание, что значение, хранящееся в числовом индексе math.pi
не учитывается, даже если число pi находится между 3 и 4, потому что оно не является целым числом.Кроме того, ни один из других нецелочисленных ключей также не учитывается.Это означает, что простой цикл for может выполнять итерацию по всему списку:
for i in 1,#unsortable do
print(i,unsortable[i])
end
Хотя это часто пишется как
for i,v in ipairs(unsortable) do
print(i,v)
end
Короче говоря, таблицы Lua представляют собой неупорядоченные наборы значений, каждое из которых индексируется ключом;но существует специальное соглашение для последовательных целочисленных ключей, начинающихся с 1.
Редактировать: Для особого случая нецелых ключей с подходящим частичным упорядочением существует обходной путь, включающий отдельную таблицу индексов.Описанное содержимое таблиц, разделенных строковыми значениями, является подходящим примером для этого трюка.
Сначала соберите ключи в новую таблицу, в виде списка.То есть создайте таблицу, проиндексированную последовательными целыми числами, начинающимися с 1, с ключами в качестве значений и отсортируйте их.Затем используйте этот индекс для перебора исходной таблицы в нужном порядке.
Например, вот foreachinorder()
, который использует этот метод для перебора всех значений таблицы, вызывая функцию для каждой пары ключ / значение, в порядке, определяемом функцией сравнения.
function foreachinorder(t, f, cmp)
-- first extract a list of the keys from t
local keys = {}
for k,_ in pairs(t) do
keys[#keys+1] = k
end
-- sort the keys according to the function cmp. If cmp
-- is omitted, table.sort() defaults to the < operator
table.sort(keys,cmp)
-- finally, loop over the keys in sorted order, and operate
-- on elements of t
for _,k in ipairs(keys) do
f(k,t[k])
end
end
Он создает индекс, сортирует его с помощью table.sort()
, затем перебирает каждый элемент в отсортированном индексе и вызывает функцию f
для каждого из них.Функция f
передается ключ и значение.Порядок сортировки определяется необязательной функцией сравнения, которая передается в table.sort
.Он вызывается с двумя элементами для сравнения (ключами к таблице t
в этом случае) и должен вернуть true
если первое меньше второго.Если опущено, table.sort
использует встроенный <
оператор.
Например, приведена следующая таблица:
t1 = {
a = 1,
b = 2,
c = 3,
}
тогда foreachinorder(t1,print)
С принтами:
a 1 b 2 c 3
и foreachinorder(t1,print,function(a,b) return a>b end)
С принтами:
c 3 b 2 a 1
Потому что у них вообще нет никакого порядка.Это все равно что пытаться рассортировать мусорный пакет, набитый бананами.