Ruby: how to sort array of string parsing the content -


here problem: have array of string contains data that:

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

i sort array scanning "data inside": here integers in brace. so, final array should :

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

is there nice solution in ruby? or should recreate new array inserts each parsed elements?

edit:

i failed mention sort priorities: first, sorting based on number in braces, 3 groups, cannot absent.

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

if first numbers equal, second ones should compared. examples:

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

but if every numbers equals, string compares. character make problem space, must after every other "visible" characters. examples:

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

i can make doing somethign in custom class:

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         = 0         until == min           if self.text[i].chr == ' ' #.chr compatibility 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 

it works fine, frustrated coding in ruby code in c++ project. why know how use "custom sort in foreach method" more complexe sort way (require parsing, scanning, regexp) naive 1 based on attribute of content.

[edit: initial solution, follows edit, not work revised statement of question. leave it, however, might of interest regardless.

the following way perform sort under revised rules, understand them. if have misinterpreted rules, expect fix minor.

regex use

let's start regex i'll use:

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

examples:

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 method

we can write method custom_sort follows (change sort_by sort_by! custom_sort!):

class array   def custom_sort     sort_by |s|       = s.scan(r).flatten       raise syntaxerror,         "'#{s}' contains empty braces" if a.any?(&:empty?)       raise syntaxerror,         "'#{s}' contains 0 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 

examples

let's try it:

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

remove "{}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 

remove "{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 0 or > 3 pair of braces 

remove "[]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"]  

explanation

suppose 1 of strings sorted was:

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

then in sort_by block, compute:

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 te"  v = u.tr(' ', 255.chr)   #=> "wr\xffi\xffte"  b << v   #=> [54.0, 31.24, 0.2, "wr\xffi\xffte"]  

note use of string#tr (or use string#gsub) puts spaces @ end of sort order of ascii characters:

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

tide]

i have assumed that, in sorting, pairs of strings compared in manner analogous array#<=>. first comparison considers strings of digits within the first pair of braces in each string (after conversion float). ties broken comparing strings of digits in second pairs of braces (converted floats). if there still tie, third pairs digits enclosed in braces compared, etc. if 1 string has n pairs of braces , has m > n pairs, , values within braces same first n pairs, assume first string precede second in sort.

code

r = /     \{    # match char     (\d+) # capture digits     \}    # match char     +     # capture 1 or more times     /x  class array   def custom_sort!     sort_by! { |s| s.scan(r).map { |e| e.first.to_f } }   end end 

example

array = ["{109}{08} ok",          "{109}{07} ok",          "{98} thx",          "{108}{0.8}{908} aa",          "{108}{0.8}{907} aa",          "{8}{51} lorem ipsum"]  = 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 ==   #=> true 

explanation

let's calculate value in array#sort_by!'s block first element of array

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

let's same other strings , put results in array:

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]]]  

sort_by in custom_sort! therefore equivalent to:

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"] 

Comments

Popular posts from this blog

shopping cart - Page redirect not working PHP -

php - How to modify a menu to show sub-menus -

python - Installing PyDev in eclipse is failed -