7

I have hash (@post) of hashes where I want to keep the order of the hash's keys in the array (@post_csv_order) and also want to keep the relationship key => value in the array.

I don't know the final number of both @post hashes and key => value elements in the array.

I don't know how to assign the hash in a loop for all elements in the array. One by one @post_csv_order[0][0] => @post_csv_order[0][1] works nicely.

#  require 'rubygems'
require 'pp'

@post = {}

forum_id = 123           #only sample values.... to make this sample script work
post_title = "Test post"

@post_csv_order = [
  ["ForumID" , forum_id],
  ["Post title", post_title]  
]

if @post[forum_id] == nil
  @post[forum_id] = {
    @post_csv_order[0][0] => @post_csv_order[0][1],
    @post_csv_order[1][0] => @post_csv_order[1][1]
    #@post_csv_order.map {|element| element[0] => element[1]}
    #@post_csv_order.each_index {|index|        @post_csv_order[index][0] => @post_csv_order[index][1] }
  }
end

pp @post

desired hash assignment should be like that

{123=>{"Post title"=>"Test post", "ForumID"=>123}}

Radek
  • 13,813
  • 52
  • 161
  • 255

3 Answers3

5

The best way is to use to_h:

[ [:foo,1],[:bar,2],[:baz,3] ].to_h  #=> {:foo => 1, :bar => 2, :baz => 3}

Note: This was introduced in Ruby 2.1.0. For older Ruby, you can use my backports gem and require 'backports/2.1.0/array/to_h', or else use Hash[]:

array = [[:foo,1],[:bar,2],[:baz,3]]
# then
Hash[ array ]  #= > {:foo => 1, :bar => 2, :baz => 3}

This is available in Ruby 1.8.7 and later. If you are still using Ruby 1.8.6 you could require "backports/1.8.7/hash/constructor", but you might as well use the to_h backport.

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
  • @Marc-Andre Lafortune: it looks nice. What would be the code in my case? `@post[forum_id] = @post_csv_order` gives me hash of arrays not hash of hashes. – Radek Jan 24 '10 at 21:56
  • You did you call Hash[] ? See my updated answer. Don't forget to require backports if using 1.8.6 – Marc-André Lafortune Jan 24 '10 at 22:03
  • I tried `@post[forum_id] = Hash[ @post_csv_order ]` and now I tried `@post[forum_id] ||= Hash[ @post_csv_order ]`. Both gives me `odd number of arguments for Hash (ArgumentError)` – Radek Jan 24 '10 at 22:17
  • hm, why did you use `||` in front of = ? – Radek Jan 24 '10 at 22:18
  • odd number of arguments => I believe you are using Ruby 1.8.6? You need to `require "backports"` beforehand. ||= see http://stackoverflow.com/questions/63998/hidden-features-of-ruby/1941479#1941479 – Marc-André Lafortune Jan 25 '10 at 01:56
  • @Marc-Andre Lafortune: you're right,it's working now.Thank you. – Radek Jan 25 '10 at 02:40
1

I am not sure I fully understand your question but I guess you want to convert a 2d array in a hash.

So suppose you have an array such as:

array = [[:foo,1],[:bar,2],[:baz,3]]

You can build an hash with:

hash = array.inject({}) {|h,e| h[e[0]] = e[1]; h}
# => {:foo=>1, :bar=>2, :baz=>3}

And you can retrieve the keys in correct order with:

keys = array.inject([]) {|a,e| a << e[0] }
=> [:foo, :bar, :baz]

Is it what you were looking for ?

paradigmatic
  • 40,153
  • 18
  • 88
  • 147
  • @paradigmatic: maybe, maybe you didn't understand fully but you fully answered :-) All I needed was `hash = array.inject({}) {|h,e| h[e[0]] = e[1]; h}` which I changed in my sample code to `@post[forum_id] = @post_csv_order.inject({}) {|h,e| h[e[0]] = e[1]; h}` and it does exactly I wanted to do. I will check it in the whole project script tomorrow. Thank you. – Radek Jan 24 '10 at 11:07
  • 2
    The other way to build a hash from an array of arrays of key, value pairs is: Hash[*array.flatten] – Wayne Conrad Jan 24 '10 at 15:49
  • 1
    @Wayne: indeed, there is an easier way with Hash[], but the flatten is not necessary since 1.8.7 (and it is actually dangerous, if any of your key/value is an array) See my answer – Marc-André Lafortune Jan 24 '10 at 19:37
  • @Wayne Conrad: wow. `@post[forum_id] = Hash[*@post_csv_order.flatten]` works really nicely.Thank you – Radek Jan 24 '10 at 22:12
0

Answers summary

working code #1

@post[forum_id] = @post_csv_order.inject({}) {|h,e| h[e[0]] = e[1]; h}

working code #2

@post[forum_id] = Hash[*@post_csv_order.flatten]

working code #3

@post[forum_id] ||= Hash[ @post_csv_order ] #requires 'require "backports"'