2015-04-24 83 views
0

這裏是我的問題:我有一個字符串數組包含這樣的數據:紅寶石:如何排序字符串數組解析內容

array = ["{109}{08} OK", 
     "{98} Thx", 
     "{108}{0.8}{908} aa", 
     "{8}{51} lorem ipsum"] 

我想解決這陣掃描「裏面的數據」 :這裏是大括號中的整數。所以,最終陣列應該是這樣的:

array.custom_sort! => ["{8}{51} lorem ipsum", 
         "{98} Thx", 
         "{108}{0.8}{908} aa", 
         "{109}{08} OK"] 

在Ruby中有沒有一個很好的解決方案?或者我應該重新創建一個插入每個解析元素的新數組?

編輯:

我沒有提到的排序優先級: 首先,排序是基於在大括號的數量,最多3組,但不能缺席。

["{5}something", 
"{61}{64}could", 
"{}be",     #raise an error or ignore it 
"{54}{31.24}{0.2}write", 
"{11}{21}{87}{65}here", #raise an error or ignore it 
"[]or",     #raise an error or ignore it 
"{31}not"] 

如果第一個數字相等,則應該比較第二個數字。 一些例子:

"{15}" < "{151}" < "{151}{32}" < "{152}" 
"{1}" < "{012}" < "{12}{-1}{0}" < "{12.0}{0.2}" 
"{5}" < "{5}{0}" < "{5}{0}{1}" 

但是,如果每一個數字是平等的,那麼該字符串進行比較。造成問題的唯一角色是空間,它必須位於每個其他「可見」字符之後。 例子:

"{1}a" < "{1}aa" < "{1} a" < "{1} a" 
"{1}" < "{1}a " < "{1}a " < "{1}a a" 
"{1}a" < "{1}ba" < "{1}b " 

我可以把它做somethign像這樣的自定義類:

class CustomArray 
    attr_accessor :one 
    attr_accessor :two 
    attr_accessor :three 
    attr_accessor :text 

    def <=>(other) 
    if self.one.to_f < other.one.to_f 
     return -1 
    elsif self.one.to_f > other.one.to_f 
     return 1 
    elsif self.two.nil? 
     if other.two.nil? 
     min = [self.text, other.text].min 
     i = 0 
     until i == min 
      if self.text[i].chr == ' ' #.chr is for compatibility with Ruby 1.8.x 
      if other.text[i].chr != ' ' 
       return 1 
      end 
      else 
      if other.text[i].chr == ' ' 
       return -1 

      #... 

    self.text <=> other.text 
    end 
end 

它工作正常,但是我非常沮喪,在Ruby中的編碼類似於C++項目,我的代碼。這就是爲什麼我想知道如何使用「foreach方法中的自定義排序」以更復雜的排序方式(需要解析,掃描,正則表達式)而不是基於內容屬性的天真方式。

回答

1

[編輯:我最初的解決方案,這是繼此編輯,不會的修訂後的聲明中工作題。不過,我會放棄它,因爲它可能是有興趣的。

以下是根據修訂後的規則執行排序的一種方法,據我所知。如果我誤解了這些規則,我預計修正會很小。

正則表達式使用

讓我們先從正則表達式,我將使用:

R =/
    \{  # match char 
    (  # begin capture group 
    \d+  # match one or more digits 
    (?:  # begin non-capture group 
    \.  # match decimal 
    \d+  # match one or more digits 
    )  # end non-capture group 
    |  # or 
    \d*  # match zero or more digits 
    )  # match end capture group 
    \}  # match char 
    /x 

例子:

a = ["{5}something", "{61}{64}could", "{}be", "{54}{31.24}{0.2}write", 
    "{11}{21}{87}{65}here", "[]or", "{31}not", "{31} cat"] 
a.each_with_object({}) { |s,h| h[s] = s.scan(R).flatten } 
    # => {"{5}something"  =>["5"], 
    # "{61}{64}could"  =>["61", "64"], 
    # "{}be"     =>[""], 
    # "{54}{31.24}{0.2}write"=>["54", "31.24", "0.2"], 
    # "{11}{21}{87}{65}here" =>["11", "21", "87", "65"], 
    # "[]or"     =>[], 
    # "{31}not"    =>["31"] 
    # "{31} cat"    =>["31"]} 

custom_sort方法

我們可以寫方法custom_sort如下(改sort_bysort_by!custom_sort!):

class Array 
    def custom_sort 
    sort_by do |s| 
     a = s.scan(R).flatten 
     raise SyntaxError, 
     "'#{s}' contains empty braces" if a.any?(&:empty?) 
     raise SyntaxError, 
     "'#{s}' contains zero or > 3 pair of braces" if a.size.zero?||a.size > 3 
     a.map(&:to_f) << s[a.join.size+2*a.size..-1].tr(' ', 255.chr) 
    end 
    end 
end 

例子

讓我們試一下:

a.custom_sort 
    #=> SyntaxError: '{}be' contains empty braces 

刪除"{}be"a

a = ["{5}something", "{61}{64}could", "{54}{31.24}{0.2}write", 
    "{11}{21}{87}{65}here", "[]or", "{31}not", "{31} cat"] 
a.custom_sort 
    #SyntaxError: '{11}{21}{87}{65}here' contains > 3 pair of braces 

刪除"{11}{21}{87}{65}here"

a = ["{5}something", "{61}{64}could", "{54}{31.24}{0.2}write", 
    "[]or", "{31}not", "{31} cat"] 
a.custom_sort 
    #=> SyntaxError: '[]or' contains zero or > 3 pair of braces 

刪除"[]or"

a = ["{5}something", "{61}{64}could", "{54}{31.24}{0.2}write", 
    "{31}not", "{31} cat"] 
a.custom_sort 
    #=> ["{5}something", 
    # "{31}not", 
    # "{31} cat", 
    # "{54}{31.24}{0.2}write", "{61}{64}could"] 

說明

假設的一個字符串進行排序是:

s = "{54}{31.24}{0.2}write a letter" 

然後在sort_by塊,我們將計算:

a = s.scan(R).flatten 
    #=> ["54", "31.24", "0.2"] 
raise SyntaxError, "..." if a.any?(&:empty?) 
    #=> raise SyntaxError, "..." if false 
raise SyntaxError, "..." if a.size.zero?||a.size > 3 
    #=> SyntaxError, "..." if false || false 
b = a.map(&:to_f) 
    #=> [54.0, 31.24, 0.2] 
t = a.join 
    #=> "5431.240.2" 
n = t.size + 2*a.size 
    #=> 16 
u = s[n..-1] 
    #=> "wr i te" 
v = u.tr(' ', 255.chr) 
    #=> "wr\xFFi\xFFte" 
b << v 
    #=> [54.0, 31.24, 0.2, "wr\xFFi\xFFte"] 

注意的是,使用String#tr(或者你可以使用String#gsub)把空間的ASCII字符排序順序的末尾:

255.times.all? { |i| i.chr < 255.chr } 
    #=> true 

潮]

我假定,在排序,對字符串的是以如下方式analo進行比較一直到Array#<=>。第一次比較考慮了每個字符串中第一對大括號內的數字字符串(轉換爲浮點數後)。通過比較第二對大括號中的數字字符串(轉換爲浮點數)來打破關係。如果還有一條平行線,則比較大括號內的第三對數字等。如果一個字符串有兩對大括號,另一個有m > n對,並且大括號內的值與第一對n對相同,則我假設第一個字符串是在排序中的第二個字符串之前。

代碼

R =/
    \{ # match char 
    (\d+) # capture digits 
    \} # match char 
    +  # capture one or more times 
    /x 

class Array 
    def custom_sort! 
    sort_by! { |s| s.scan(R).map { |e| e.first.to_f } } 
    end 
end 

array = ["{109}{08} OK", 
     "{109}{07} OK", 
     "{98} Thx", 
     "{108}{0.8}{908} aa", 
     "{108}{0.8}{907} aa", 
     "{8}{51} lorem ipsum"] 

a = array.custom_sort! 
    #=> ["{8}{51} lorem ipsum", 
    # "{98} Thx", 
    # "{108}{0.8}{907} aa", 
    # "{108}{0.8}{908} aa", 
    # "{109}{07} OK", 
    # "{109}{08} OK"] 

array == a 
    #=> true 

說明

現在讓我們來計算在Array#sort_by!的塊中的值的第一元件210

s = "{109}{08} OK" 

a = s.scan(R) 
    #=> [["109"], ["08"]] 
b = a.map { |e| e.first.to_f } 
    #=> [109.0, 8.0] 

現在讓我們做其他弦相同,結果放一個數組:

c = array.map { |s| [s, s.scan(R).map { |e| e.first.to_f }] } 
    #=> [["{8}{51} lorem ipsum", [8.0, 51.0]], 
    # ["{98} Thx",   [98.0]], 
    # ["{108}{0.8}{907} aa", [108.0, 907.0]], 
    # ["{108}{0.8}{908} aa", [108.0, 908.0]], 
    # ["{109}{07} OK",  [109.0, 7.0]], 
    # ["{109}{08} OK",  [109.0, 8.0]]] 

custom_sort!sort_by因此等效於:

c.sort_by(&:last).map(&:first) 
    #=> ["{8}{51} lorem ipsum", 
    # "{98} Thx", 
    # "{108}{0.8}{907} aa", 
    # "{108}{0.8}{908} aa", 
    # "{109}{07} OK", 
    # "{109}{08} OK"] 
+0

徹底,一如既往。好工作。 – DiegoSalazar

+0

這是非常有用的解釋,非常感謝!但是對於字符串比較,我忘了提到一些東西。假設:[「{42} foo」,「{42}酒吧」,「{42}世界」,「{42}你好」]。訣竅在於比較字符串,空格字符''應該*在* alphanum之後,但在ASCII表格中,它不會。我在另一個線程(http://stackoverflow.com/questions/29808574/ruby-custom-string-sort)中看到我可以設置自己的「字符數組」命令。是否有可能將此功能集成到您的優雅解決方案中? – Rikouchi

+0

今天晚些時候我會看看。同時,我建議你編輯你的問題,在你的評論中添加信息(並且如果需要,還可以糾正'108'和'109'的排序)。由於附加信息改變了問題,因此您需要在編輯中清楚地說明問題。通常這是通過編寫「編輯:我沒有提及...」來完成的,也許在問題的最後。 –

-2

您可以通過Array#sort定義應該如何排序元素的塊。

1

這應做到:

array.sort_by do |s| 
    # regex match the digits within the first pair of curly braces 
    s.match(/^\{(\d+)\}/)[1].to_i # convert to an int in order to sort 
end 

# => ["{8}{51} lorem ipsum", "{98} Thx", "{108}{0.8}{908} aa", "{109}{08} OK"] 
1
array.sort_by { |v| (v =~ /(\d+)/) && $1.to_i } 

交替

array.sort_by { |v| /(\d+)/.match(v)[1].to_i }