ruby string to hash conversion

14,766

Solution 1

Direct copy/past of an IRB session:

>> str.split(/, /).inject(Hash.new{|h,k|h[k]=[]}) do |h, s|
..   v,k = s.split(/@/)
..   h[k] << v
..   h
..   end
=> {"p"=>["uu"], "m"=>["xx", "zz"], "n"=>["yy"]}

Solution 2

Simpler code for a newbie :)

str = "uu@p, xx@m, yy@n, zz@m"
h = {}
str.split(",").each do |x| 
   v,k = x.split('@')
   h[k] ||= []
   h[k].push(v)
end

p h

Solution 3

FP style:

grouped = str.split(", ").group_by { |s| s.split("@")[1] }
Hash[grouped.map { |d, emails| [d, emails.map { |s| s.split("@")[0] }] }]

#=> {"m"=>["xx", "zz"], "n"=>["yy"], "p"=>["uu"]}

As usual, the solution is much shorter and clearer if you resort to Facets cool abstractions (no need to install the whole library, just copy the specific methods to your extensions module):

require 'facets'
str.split(", ").map_by { |s| s.split("@", 2).reverse }
#=> {"m"=>["xx", "zz"], "n"=>["yy"], "p"=>["uu"]}

Facets#map_by = group_by+map

Share:
14,766
Mr. Black
Author by

Mr. Black

profile for Mr. Black on Stack Exchange, a network of free, community-driven Q&amp;A sites http://stackexchange.com/users/flair/177944.png

Updated on June 28, 2022

Comments

  • Mr. Black
    Mr. Black almost 2 years

    I have a string like this,

    str = "uu@p, xx@m, yy@n, zz@m"

    I want to know how to convert the given string into a hash. (i.e my actual requirement is, how many values (before the @ symbol) have the m, n and p. I don't want the counting, I need an exact value). The output would be better like this,

    {"m" => ["xx", "zz"], "n" => ["yy"], "p" => ["uu"]}

    Can help me anyone, please?

  • Mr. Black
    Mr. Black over 12 years
    Many thanks @Michael. This is what i expect. Again, I'm exciting about the power of inject method. Can, you please explain this portion "..inject(Hash.new{|h,k|h[k]=[]}).."? It's help to improve myself.
  • Michael Kohl
    Michael Kohl over 12 years
    Glad to help! :-) If your question is sufficiently answered, please accept it so other people know.
  • Michael Kohl
    Michael Kohl over 12 years
    Sure, I'm using the block form of Hash.new (see docs) to inject a hash that has the empty array as default value.
  • Michael Kohl
    Michael Kohl over 12 years
    Aren't folds (and hence inject) functional enough for you? ;-)
  • d11wtq
    d11wtq over 12 years
    I think I prefer this for no other reason than the fact it does not involve keeping state in temporary variables.
  • tokland
    tokland over 12 years
    @Michael. Let's call it then "another FP-style answer". However, note that "h[k] << v" is hardly functional :-p Moreover, a typical FP advice is: don't resort to folds when there are other abstractions at hand (since we potentially can write almost all enumerable abstractions with folds: map/select/group_by/partition/max/min/...).
  • Michael Kohl
    Michael Kohl over 12 years
    Fair enough about mutating the array. About that piece of advice you mention: I never really heard that one before and I do Haskell and Clojure on a fairly regular basis. :-) Anyway, I like your answer, I was one of the 2 upvoters.
  • tokland
    tokland over 12 years
    @Michael. To be fair, in this case vanilla Ruby has no suitable abstraction (my first snippet splits twice, which is conceptually ugly). So, I'd go for either your inject or Facets#map_by.