在Lua中按值关联排序表

25
我是一名有用的助手,可以为你进行文本翻译。以下是需要翻译的内容:

我有一个键值表需要在Lua中进行排序。这些键都是整数,但不连续(并且具有含义)。Lua的唯一排序函数似乎是table.sort,它将表视为简单的数组,丢弃原始键及其与特定项的关联。相反,我实际上希望能够使用PHP的 asort()函数。

我所拥有的:

items = {
    [1004] = "foo",
    [1234] = "bar",
    [3188] = "baz",
    [7007] = "quux",
}

排序操作后我想要的结果:

items = {
    [1234] = "bar",
    [3188] = "baz",
    [1004] = "foo",
    [7007] = "quux",
}

有什么想法吗?

编辑:根据答案,我假设这只是我正在使用的特定嵌入式Lua解释器的奇怪习惯,但在我所有的测试中,pairs()总是按照它们添加到表中的顺序返回表项。(即上面的两个声明将迭代不同)。

不幸的是,因为这不是正常行为,看起来我无法得到我需要的内容;Lua没有必要的内置工具(当然了),而嵌入式环境过于有限,无法解决这个问题。

还是感谢大家的帮助!


7
这两张桌子完全一样。 - lhf
7个回答

43

您似乎有些误解。您所使用的是一个关联数组。关联数组本身没有明确的顺序,例如仅仅是内部表示(通常排序)才能对它们进行排序。

简而言之,在Lua中,您所发布的这两个数组是相同的

相反,您应该使用以下表示方法:

items = {
    {1004, "foo"},
    {1234, "bar"},
    {3188, "baz"},
    {7007, "quux"},
}

虽然现在您无法通过索引获取它们(它们是索引为1、2、3、4的数组,但是您可以创建另一个索引数组),但您可以使用table.sort对它们进行排序。

排序函数应该如下:

function compare(a,b)
  return a[1] < b[1]
end

table.sort(items, compare)

2
这似乎对Lua不适用,根据我的测试结果。当使用pairs()迭代表时,顺序是稳定的,并且与添加项目的顺序对应。此外,我没有更改数据存储方式的选项;我正在将结果提供给第三方库,在“pairs()顺序”中向用户显示项目。 - Ben Blank
5
pairs函数按哈希顺序返回数据,可能是稳定的,但不是添加项目的顺序。 来自lua-users wiki:“请注意,在使用字典时,无法保证键将以何种顺序存储在表中,因此使用pairs()检索键的顺序不受保证。即使是在表的索引部分或者一个没有用作字典而只有指数为键的表中也适用这个警告。” - sylvanaar
我已经更新了问题,但是现在我愿意相信我正在使用的特定嵌入式解释器只是有些奇怪。 :-) - Ben Blank
8
顺便说一下,“return a[0] < b[0]” 这个代码让我也犯了很多错。Lua 索引是从 1 开始的。 - Stavros Korokithakis
1
@Stavros - 哇,真奇怪居然要这么久才有人注意到它! - Kornel Kisielewicz
2
既然我们想要比较字符串,那么应该是 return a[2] < b[2] 吧? - phil294

13

正如Komel所说,你正在处理关联数组,这些数组没有保证的排序。

如果你想要基于与其相关联的值进行键排序同时保留关联数组的功能,你可以这样做:

function getKeysSortedByValue(tbl, sortFunction)
  local keys = {}
  for key in pairs(tbl) do
    table.insert(keys, key)
  end

  table.sort(keys, function(a, b)
    return sortFunction(tbl[a], tbl[b])
  end)

  return keys
end

items = {
    [1004] = "foo",
    [1234] = "bar",
    [3188] = "baz",
    [7007] = "quux",
}

local sortedKeys = getKeysSortedByValue(items, function(a, b) return a < b end)

sortedKeys是{1234,3188,1004,7007},您可以按以下方式访问您的数据:

for _, key in ipairs(sortedKeys) do
  print(key, items[key])
end

结果:

1234     bar     
3188     baz     
1004     foo     
7007     quux    

你真是救了我的一天,我已经为游戏中表格的排序(例如排名)挣扎了几个小时了。这些可恶的排序... - Tosfera

6

哦,我错过了不能控制迭代的部分。 但在lua中通常总有一种方法。

http://lua-users.org/wiki/OrderedAssociativeTable

这是一个开始。 现在你需要替换库使用的pairs()。 这可能只是简单的pairs=my_pairs。 然后,您可以使用上面链接中的解决方案。


3

PHP数组与Lua表格不同。

  • PHP数组可以有一个键值对的有序列表

  • Lua表格始终包含一个无序的键值对集合

当程序员选择使用整数1、2、3等作为键时,Lua表格就像一个数组。语言语法和标准库函数(如table.sort)为具有连续整数键的表格提供特殊支持。

因此,如果您想模拟PHP数组,您需要使用键值对的列表来表示它,这实际上是一个表格的表格,但将其视为键值对的列表更有帮助。向table.sort传递自定义的“小于”函数即可完成设置。

N.B. Lua允许您在同一表中混合连续整数键和任何其他类型的键,并且表示效率很高。我有时使用此功能,通常是为数组添加一些元数据标记。


3

几个月后再次来到这里,有同样的问题。推荐的答案似乎指出了所需内容与LUA中实际情况之间的差距,但并没有完全满足我的要求——即按键排序的哈希表。

然而,这个页面上的前三个函数确实做到了:http://lua-users.org/wiki/SortedIteration


2
但这恰恰相反:这个问题是按值排序。你的答案是按键排序。 - bluenote10

1

我几年前做过一些Lua编程,但现在已经不熟悉了。

当遇到类似的问题时,我将数组复制到另一个数组中,然后反转键和值,最后使用sort对新数组进行排序。

我不知道使用Kornel Kisielewicz推荐的方法来对数组进行排序。


1
提出的“compare”函数有效,但仅当第一列中的值唯一时才有效。
这里有一个稍微增强了的“compare”函数,以确保如果实际列的值相等,则取下一列的值进行评估...
对于{1234,“baam”} < {1234,“bar”}为true,包含“baam”的数组将被插入在包含“bar”的数组之前。
local items = {
    {1004, "foo"},
    {1234, "bar"},
    {1234, "baam"},
    {3188, "baz"},
    {7007, "quux"},
}

local function compare(a, b)
    for inx = 1, #a do
        -- print("A " .. inx .. " " .. a[inx])
        -- print("B " .. inx .. " " .. b[inx])

        if a[inx] == b[inx] and a[inx + 1] < b[inx + 1] then
            return true
        elseif a[inx] ~= b[inx] and a[inx] < b[inx] == true then
            return true
        else
            return false
        end
    end
    return false
end

table.sort(items,compare)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接