I have a string that contains:

Product Name : 1 year technical support - Win forms + Web forms Edition - Single Product ID : 40133 Order ID : 469224-001-1UF Order Date : 5/12/2001 7:44 AM Quantity : 1 Backup CD : No Download Warranty : No Coupon used : Currency : USD Unit Price : 10.00 USD Unit Discount : Volume Discount : Total Discount : Total Product Price : 10.00 USD 

It is necessary to add each line to the hash, while the value before the colon must be a key

  • 2
    Anything that does not have a single comma? - Qwertiy

2 answers 2

We take an iterator on separate lines (so as not to store the entire broken array in the memory, but to take the podstvorochki one by one; this is often saving on matches, but it can have a good effect on a huge data set):

 str.each_line 

And we try to shove each line into an empty hash ...

  .each_with_object({}) do |line, hash| 

...in the following way:

We get the key and value from the line (cutting off the spaces: I think this is useful here):

  key, value = line.split(':', 2) # Разбить по двоеточию не более чем на 2 части .map(&:strip) # Подстричь оба от пробельных символов # Ура декомпозиции! # `a, b = [1, 2]` то же, что и `a = 1; b = 2` 

And if the value found (not nil , which can be for strings without : , we push it into our hash:

  hash[key] = value unless value.nil? 

It's all over.

  end 

The return value will be the desired hashmap.

Of course, this is Ruby, and you can make many different variations on the subject. Say, if you want to do some postprocessing value, instead of wrapping most of the block in unless value.nil? , can I do next if value.nil? , and the remainder of the block will be simply omitted in the case of an missing value.

    A variation on the topic :)

     $str = <<-EOT Product Name : 1 year technical support - Win forms + Web forms Edition - Single Product ID : 40192 Order ID : 469074-001-1UF Order Date : 5/12/2011 7:44 AM Quantity : 1 Backup CD : No Download Warranty : No Coupon used : Currency : USD Unit Price : 109.00 USD Unit Discount : Volume Discount : Total Discount : Total Product Price : 109.00 USD EOT $out = Hash.new() # Собираем хэш (немножко упростилось) :) $str.each_line do |x| $out[$m[1]] = $m[2] if $m = (/^\s*([^:]*?)\s*:\s*(.*?)\s*$/).match(x) end # Печатаем хэш $out.each do |key, value| puts key + ' : ' + value end 
    • Nothing so, yes. True, .strip can be .strip into account in the regular .strip and removed. And I'm still processing blank lines (were in the first edition of the question), your implementation can, uh, fall on them without finding .strip in nil . But yes, in the last edition of the question they really are not. And yes, you will notice that I don’t really like regulars :) - D-side
    • The strip in the current formulation of the problem does not work in the regulars stick. Strange ruby ​​works - if the value after ":" is empty, then ruby ​​fills only two matches, and Pearl fills three, where the second fills in an empty line. Well, or I did not even understand. Therefore, capturing spaces, then to clean them. - Majestio
    • "I know, I'll use regular expressions!" "Now you have two problems ...": D Why are there three groups of capture, by the way, and not two? And why didn’t they make money for the beginning and end of the line? - D-side
    • sense to ankerit, if I constantly bite off the tail and it is in a loop further parsya - at the end a greedy search for the tail - Majestio
    • stop .... yes, it is necessary to remove the 3rd group of capture - from the old version with while still left, I will redo it - Majestio