混合字母和数字的实数列表排序

3

我有一个数据列表需要排序,但遗憾的是这些对象的命名方案不是非常一致。这个数据是一个字符串列表,其中大多数情况下为实数,但有时在末尾加上字母。以下是此列表中可接受的值的一些示例:

# this is how it should be sorted
['1', '1.1', '1.2', '2', '2.1A', '2.1B', '2.2A', '101.1', '101.2']

由于这些数据存储在数据库中,我的第一个想法是使用以下Django方法返回排序的结果,但它会按以下方式返回。

#took out unneeded code
choices = [l.number for l in Locker.objects.extra(
               select={'asnumber': 'CAST(number as BYTEA)'}).order_by('asnumber')]
print choices
==> ['1', '1.1', '101.1', '101.2', '2', '2.1A', '2.1B', '2.2A']

很遗憾,它不能按照正确顺序进行排序。因此,我的新计划是编写一个方法,该方法将与Python的sorted方法配合使用,但我仍然不确定如何编写。我需要找到一种方法来按字符串的实数部分排序,然后作为次要排序方式,按附加在末尾的字母排序。
有关如何处理此问题的任何建议吗?

只有数字、点和 AB 或更多字符吗? - Erwin Brandstetter
1
你说你想要按照实数的方式对前面进行排序,但是我不禁想知道这是否更像是版本号的主次部分。你想让“1.3”排在“1.12”之前还是之后? - kgrittn
啊,你说得对,我应该把“实数”这个词解释得更具体一些。它更像是一个小版本号,但希望永远不会出现1.12这样的版本号,但如果真有的话,它应该排在其他版本之前。我明天再来看看哪个答案最好。 - Bob
5个回答

4
让数据库管理系统来排序,这是它非常擅长的。你几乎无法在应用程序中与其性能相媲美。
如果你只有带有 A 或 B 的小数,可以简单地执行以下操作:
SELECT *
FROM  (
   SELECT unnest(
    ARRAY['1', '1.1', '1.2', '2', '2.1A', '2.1B', '2.2A', '101.1', '101.2']) AS s
   ) x
ORDER  BY rtrim(s, 'AB')::numeric, s;

订单完全按照要求,并且速度也很快。使用ARRAYunnest()的子查询仅用于构建快速测试用例。 ORDER BY子句是最重要的-请参阅手册中的rtrim()

如果涉及其他字符,则可能需要更新您的问题以完整呈现情况。


对于更棘手的数据,您可以编写一个 PL/PgSQL 函数来生成排序键。 - Craig Ringer

2
x = ['1', '1.1', '1.2', '2', '2.1A', '2.1B', '2.2A', '101.1', '101.2']

#sort by the real number portion

import string

letters = tuple(string.ascii_letters)

def change(x):
    if x.endswith(letters):
        return float(x[:len(x) -1])
    else:
        return float(x)

my_list = sorted(x, key = lambda k: change(k))

结果:

>>> my_list
['1', '1.1', '1.2', '2', '2.1A', '2.1B', '2.2A', '101.1', '101.2']

0

我过早地泛化了结尾的任意数量字母:

from itertools import takewhile

def sort_key(value):
    cut_point = len(value) - len(list(takewhile(str.isalpha, reversed(value))))
    return (float(value[:cut_point]), value[cut_point:])

sorted((
    l.number
    for l in Locker.objects.extra(select={'asnumber': 'CAST(number as BYTEA)'})
), key = sort_key)

0

将字符串拆分为元组 - 一个实数(将其转换为浮点数或十进制数)和一个通常为空的字符字符串。如果对元组进行排序,并使用Python的内置排序(timesort),它应该非常快。

如果您的实数允许科学计数法,例如1e10,请小心。

如果有任何可能会在以后的比较中增加复杂性,请使用类而不是元组。但是元组可能会更快。然后定义一个或多个比较函数(取决于您是否在python 2.x或3.x中)。

元组按元素0、元素1等进行比较。

您的类替代方法需要具有cmp方法或3.x等效方法。


0

将字符串存储为字符串,然后解析它以进行排序似乎是错误的方法。如果您真正拥有的是:

  • 主要编号
  • 次要编号
  • 可选修订版号

那么我强烈建议将其存储为两个整数和一个文本字段。按major_number、minor_number、revision排序将完全按预期工作。您可以在数据库级别上将asnumber定义为视图,也可以将其定义为基于三个基本数字的类,并附带一个关联的__cmp__()


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