按照字母顺序对Lua中的表进行排序,但忽略数字。

5

我希望能够按字母顺序对表格进行排序,但不包括数字。

下面的代码展示了如何使用比较函数对表格进行排序:

function( a,b ) return a.N < b.N end

给我:
obj = {
    [1] = {
        ["N"] = "Green 1";
    };
    [2] = {
        ["N"] = "Green 11";
    };
    [3] = {
        ["N"] = "Green 2";
    };
    [4] = {
        ["N"] = "Red 1";
    };
}

但是我希望按照以下方式进行排序:
obj = {
    [1] = {
        ["N"] = "Green 1";
    };
    [2] = {
        ["N"] = "Green 2";
    };
    [3] = {
        ["N"] = "Green 11";
    };
    [4] = {
        ["N"] = "Red 1";
    };
}

这是可能的吗?


在排序之前,从键中删除所有数字?仅按第一个单词排序?如果您的表很大或者您经常这样做,保留您生成的“排序键”的缓存可能是个好主意。 - Etan Reisner
3个回答

6

试试这个:

local function split(a)
    local x,y=a.N:match("(%S+)%s+(%S+)")
    return x,tonumber(y)
end

table.sort(obj,
    function (a,b)
        local a1,a2=split(a)
        local b1,b2=split(b)
        return a1<b1 or (a1==b1 and a2<b2)
    end
)

有个问题,没有提到表格中的一些字符串没有数字。所以我得到一个错误。 - Dreanh
在这一行代码上出现了错误:return a1<b1 or (a1==b1 and a2<b2) - Dreanh
1
好的,已修复:local function split(a) if string.match(a.N, '%d+') then local x,y=a.N:match("(%D+)(%d+)") return x,tonumber(y) else local x,y = a.N,0 return x,y end end - Dreanh

3

最初我打算发布这篇文章,但lhf提供的解决方案已经回答了你的问题。既然你还有问题,请尝试以下方法。

local function cmp(a, b)
   a = tostring(a.N)
   b = tostring(b.N)
   local patt = '^(.-)%s*(%d+)$'
   local _,_, col1, num1 = a:find(patt)
   local _,_, col2, num2 = b:find(patt)
   if (col1 and col2) and col1 == col2 then
      return tonumber(num1) < tonumber(num2)
   end
   return a < b
end

local obj = {
   { N = '1'           },
   { N = 'Green1'      }, -- works with optional space
   { N = 'Green'       }, -- works when doesn't fit the format
   { N = 'Sky blue99'  },
   { N = 'Green 11'    },
   { N = 'Green 2'     },
   { N = 'Red 02'      }, -- works when has leading zeros
   { N = 'Red    01'   }, -- works with padding spaces
   { N = 'Sky blue 42' }, -- works with multi-word color names
   { N = 99            }, -- works with numbers
}

table.sort(obj, cmp)
for i,v in ipairs(obj) do
   print(i, v.N)
end

输出:

1   1
2   99
3   Green
4   Green1
5   Green 2
6   Green 11
7   Red    01
8   Red 02
9   Sky blue 42
10  Sky blue99

谢谢,这个效果更好。将模式改为:'^(%D+)(%d+)$'以允许 { N = 'Green1' },看起来可以工作,但可能会漏掉某些东西。 - Dreanh
@Dreanh 那个模式看起来不错,只要颜色名称中没有数字,(.+) 就可以匹配任何字符。 - Adam
好的,谢谢。如果我使用'^(.+)(%d+)$''^(%g)(%d+)$',它不起作用。 - Dreanh
啊,好了!不过又发现了一个问题,如果 N = 1 的话可以绕过去,但是如果那个也能正常工作就更好了。 - Dreanh
@Dreanh 好的,已更新以处理数字。这超出了您最初问题的范围,您可能需要更新它以指定是否需要处理空格和数字。 - Adam

3

@lhf的解决方案应该适用于您,但您可能需要考虑是否需要处理一些特殊情况,比如将“Green 1”与“Green 02”进行比较或将“Green 2”与“Green 02”进行比较。我已经查阅了几种实现字母数字排序的方法,并在博客文章中比较了它们的结果。您还可以查看lua邮件列表上关于这个话题的讨论。


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