1

please help me to make method named hash_string so that it takes the hash and make a string from its key/value pairs. Example:

hash_string({id: 8, name: 'marry'}) # should return "id = 8, name = marry"

I've tried same way before, they are:

def hash_string(hash)
  hash.to_s
end

and

def hash_string(hash)
  set_value = hash.each {|key, value| puts "#{key} = #{value}" }
  # set_value.join(",")
end

but they did not work. Will you please explain me, how can i do that?

3 Answers 3

3
def hash_string(hash); hash.map{|e| e.join(" = ")}.join(", ") end

When map applies to a hash, to_a is applied, which gives key-value pairs. Each pair is captured by a single block variable e, to which join applies.

Sign up to request clarification or add additional context in comments.

1 Comment

Sawa,thanks for your explanation. Now i clearly understand how it works.
1

Replace each with map, return the value instead of outputting it, and you're done.

def hash_string(hash)
  set_value = hash.map {|key, value| "#{key} = #{value}" }
  set_value.join(",")
end

Comments

1

Is there another way? (You did ask.) I think this should work. Counterexample, anyone?

h = {:id=>8, name: "marry", "age"=>25, 7=>4}
  #=> {:id=>8, :name=>"marry", "age"=>25, 7=>4}

h.to_s[1..-2].gsub(/:?([^:]+?)=>/,'\1 = ').gsub('"', '')
  #=> "id = 8, name = marry, age = 25, 7 = 4"
  • h.to_s[1..-2] converts the hash to a string and strips off the first ({) and last (}) characters.
  • :? consumes a : if one is present immediately before the capture group that follows.
  • ([^:]+?) is capture group #1, which captures one or more characters other than :, non-greedily (signified by ?) and is followed by =>. Alternatively, one could have /:?([^:=>]+)=>/.
  • The match, if there is one, is replaced by '\1 = ', which is the contents of the capture group, followed by " = ". This could instead be written with double-quotes ("\\1 = ").

Let's walk through an example:

h = {:id=>8, "name"=>"mary"}             #=>  {:id=>8, "name"=>"mary"}
str0 = h.to_s                            #=> "{:id=>8, \"name\"=>\"mary\"}"
str1 = str0[1..-2]                       #=>  ":id=>8, \"name\"=>\"mary\""
str2 = str1.gsub(/:?([^:]+?)=>/,'\1 = ') #=>   "id = 8, \"name\" = \"mary\""
str2.gsub('"', '')                       #=>   "id = 8, name = mary"

Now let's have a closer look at the penultimate statement.

gsub is looking for substrings of:

":id=>8, \"name\"=>\"mary\""

that match the regex:

/:?([^:]+?)=>/

Whenever it finds one, it replaces it with \1 =, where \1 denotes the contents of the regex's one and only capture group.

In :?, the ? means "match a : if one is present. It finds one, before id. It now moves on to the "capture group" (capture group #1), designated by the parenthesis: ([^:]+?). [^:]+? captures one or more characters other than :.

As the capture group is followed by =>, it will capture matching characters until it reaches =>. But which pair, the one in id=>8 or the one in \"name\"=>\"mary\"? Regex's are naturally "greedy", so if the capture group were ([^:]+), it would match the last one, capturing id=>8, \"name\". To prevent that from happening, we add the ? to make the match on [^:]+? "non-greedy", this is, stopped by the first => it encounters, causing it to match just id.

gsub has matched :id=>, of which id is the contents of the capture group, which can be reference in a string by \1. It therefore replaces :id=> with \1 = => id =.

Wait a minute. The capture group does not match :, so why do we need :? at the beginning of the regex? Let's try without that:

str2 = str1.gsub(/([^:]+?)=>/,'\1 = ') #=> ":id = 8, \"name\" = \"mary\""

As you see, the first : was not removed. That's because it was not part of the match that gsub replaced with \1 =. Hence the need for :?.

The last statement merely converts each " to an empty string (i.e., removes the double quotes).

4 Comments

Since you seem to be looking for a counterexample: h = {"=>" => "foo"} gives " = = foo".
@Cary Swoveland, Although I've read the document link, I did not understand that (.gsub) method . Would you please explain to me, how it works?
Rabby, your wish is my command. Note that the elaboration of my explanation is meant to give you a better understanding of how regex's work, and not so much about your question.
Thanks, @sawa. I figured there could be a few. I did not mean this to be a serious option (what I meant by "You did ask"), but thought it might have some educational value. I see { ":"=>"foo" } => " = foo" is problematic as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.