2012-04-24 67 views
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方法一起工作,但我仍然不確定如何去寫這個。我需要找到一種方法來根據字符串的實數部分進行排序,然後將其作爲次要排序,按附加到結尾的字母進行排序。

任何建議去哪裏與此?

+0

只是數字,點和'A'或'B'或更多的字符? – 2012-04-25 00:33:00

+1

你說你想讓前臺像真正的號碼一樣排序,但我不禁想知道它是否更像是版本號的主要部分和次要部分。你想要'1.3'在'1.12'之前還是之後排序。 – kgrittn 2012-04-25 02:13:33

+0

啊你是對的,我應該比「真實」的數字更具體。它更像是一個小版本號,但希望永遠不會有1.12,但如果有的話應該先到另一個。我將不得不在明天檢查以查看哪個答案效果最好 – Bob 2012-04-25 04:36:25

回答

4

讓DBMS做排序,這就是它非常擅長的。你幾乎無法與你的應用程序的性能相媲美。

如果你得到的是與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() in the manual

如果還涉及其他字符,您可能需要更新您的問題以完成圖片。

+0

對於更復雜的數據,您可以編寫一個生成排序鍵的PL/PgSQL函數。 – 2012-04-25 22:15:01

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。

1
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

存儲的字符串作爲一個字符串,然後解析它梳理它似乎是錯誤的做法。如果你真的有

  • 主設備號
  • 次編號
  • 可選的版本

然後我會強烈建議把它作爲兩個整數和一個文本字段。在major_number,minor_number上進行排序,修正將完全按預期工作。您可以將asnumber定義爲數據庫級別的視圖,也可以將三個基本數字與關聯的__cmp__()相關聯。