Flattening hash into string in Ruby

RubyHash

Ruby Problem Overview


Is there a way to flatten a hash into a string, with optional delimiters between keys and values, and key/value pairs?

For example, print {:a => :b, :c => :d}.flatten('=','&') should print a=b&c=d

I wrote some code to do this, but I was wondering if there was a neater way:

class Hash
  def flatten(keyvaldelimiter, entrydelimiter)
    string = ""
    self.each do
      |key, value|
      key = "#{entrydelimiter}#{key}" if string != "" #nasty hack
      string += "#{key}#{keyvaldelimiter}#{value}"  
    end
    return string
  end
end

print {:a => :b, :c => :d}.flatten('=','&') #=> 'c=d&a=b'

Thanks

Ruby Solutions


Solution 1 - Ruby

I wouldn't override .flatten, which is already defined:

> Returns a new array that is a > one-dimensional flattening of this > hash. That is, for every key or value > that is an array, extract its elements > into the new array. Unlike > Array#flatten, this method does not > flatten recursively by default. If the > optional level argument determines the > level of recursion to flatten.

This is simplest way to do it that I'm aware of:

{:a => 100, :b => 200}.map{|k,v| "#{k}=#{v}"}.join('&')



=> "a=100&b=200"

=> "a=100&b=200"

Solution 2 - Ruby

Slight variation of @elektronaut's version:

You can actually put just an |e| there instead of |k, v| in which case e is an array of two elements and you can call e.join('='). Altogether you have something like

class Hash
  def join(keyvaldelim=$,, entrydelim=$,) # $, is the global default delimiter
    map {|e| e.join(keyvaldelim) }.join(entrydelim)
  end
end

{a: 100, b: 200}.join('=', '&') # I love showing off the new Ruby 1.9 Hash syntax
# => 'a=100&b=200'

Solution 3 - Ruby

If you're trying to generate a url query string, you most certainly should use a method like activesupport's to_param (aliased to to_query). Imagine the problems if you had an ampersand or equal sign in the data itself.

to_query takes care of escaping:

>> require 'active_support/core_ext/object'
>> {'a&' => 'b', 'c' => 'd'}.to_query
>> => "a%26=b&c=d"

EDIT

@fahadsadah makes a good point about not wanting to load Rails. Even active_support/core_ext/object will load 71 files. It also monkey-patches core classes.

A lightweight and cleaner solution:

require 'rack'  # only loads 3 files
{'a&' => 'b', 'c' => 'd'}.map{|pair|
  pair.map{|e| Rack::Utils.escape(e.to_s) }.join('=')
}.join('&')
# => "a%26=b&c=d"

It's important to escape, otherwise the operation isn't reversible.

Solution 4 - Ruby

Well, you could do it with standard methods and arrays:

class Hash
  def flatten(keyvaldelimiter, entrydelimiter)
    self.map { |k, v| "#{k}#{keyvaldelimiter}#{v}" }.join(entrydelimiter)
  end
end

You might also be interested in the Rails to_query method.

Also, obviously, you can write "#{k}#{keyvaldelimiter}#{v}" as k.to_s + keyvaldelimiter + v.to_s...

Solution 5 - Ruby

Not sure if there's a built-in way, but here's some shorter code:

class Hash
  def flatten(kvdelim='', entrydelim='')
    self.inject([]) { |a, b| a << b.join(kvdelim) }.join(entrydelim)
  end
end

puts ({ :a => :b, :c => :d }).flatten('=', '&') # => a=b&c=d

Solution 6 - Ruby

What about getting it in JSON format. That way the format is clear.

There are lots of JSON tools for ruby. Never tried them, though. But the output will then of course slide into javascript, etc easier. Likely one line.

The big savings, though would be in documentation, in that you will need much less.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFahad SadahView Question on Stackoverflow
Solution 1 - RubyelektronautView Answer on Stackoverflow
Solution 2 - RubyJörg W MittagView Answer on Stackoverflow
Solution 3 - RubyKelvinView Answer on Stackoverflow
Solution 4 - RubyAmadanView Answer on Stackoverflow
Solution 5 - RubysarahhodneView Answer on Stackoverflow
Solution 6 - RubyTom AndersenView Answer on Stackoverflow