0

After reading a csv file, I have this data structure:

[["name1 | value1 | value2 | value3 | value4 "],
 ["name2 | value1 | value2 | value3 | value4 "],...]

I need to convert this to a Hash, like this:

{"name1" => "value1 | value2 | value3 | value4", 
 "name2" => "value1 | value2 | value3 | value4",...}

Or, better yet:

{"name1" => ["value1","value2","value3","value4"],
 "name2" => ["value1","value2","value3","value4"],...}

I have found numerous methods for converting arrays of arrays to hashes, but none that take the first element in the inner array and use it as the key of the Hash.

Can anyone suggest an elegant solution?

4 Answers 4

2
rows = [["name1 | value1 | value2 | value3 | value4 "],
        ["name2 | value1 | value2 | value3 | value4 "],]

h = Hash[
   rows.flatten.
   map { |r| r = r.split('|').map(&:strip); [r.first, r.drop(1)] }
]

# => {"name1"=>["value1", "value2", "value3", "value4"],
#     "name2"=>["value1", "value2", "value3", "value4"]}

I'm a bit curious how you arrived at that input structure. If you read the CSV with delimiter '|', you would probably start with something more like:

[["name1", "value1", "value2", "value3", "value4"],
 ["name2", "value1", "value2", "value3", "value4"]]

Which is what the r = r.split('|').map(&:strip) in the outer map is about transforming to. If you did have this form, the conversion to hash is much simpler:

Hash[ rows.map { |r| [r.first, r.drop(1)] } ]
Sign up to request clarification or add additional context in comments.

3 Comments

Or using the splat operator: Hash[rows.map { |name, *values| [name, values] }]
@Stefan Your comment should be an independent answer.
@sawa I've posted it as an answer.
1

In addition to @dbenhur's answer. Assuming that the CSV class can handle the delimiters and you get a base structure like:

rows = [["name1", "value1", "value2", "value3", "value4"],
        ["name2", "value1", "value2", "value3", "value4"]]

You can convert it using the splat operator:

Hash[rows.map { |name, *values| [name, values] }]
# => {"name1"=>["value1", "value2", "value3", "value4"], "name2"=>["value1", "value2", "value3", "value4"]}

Comments

0
def to_hash(a)
  h = {}
  a.each do |item|
    a2 = item.first.strip.split(' | ')
    h[a2.shift] = a2
  end
  h
end

Usage:

a = [["name1 | value1 | value2 | value3 | value4 "],
     ["name2 | value1 | value2 | value3 | value4 "]]
to_hash(a)
# => {"name1"=>["value1", "value2", "value3", "value4"],
#     "name2"=>["value1", "value2", "value3", "value4"]}

Comments

0

You could try this:

csv.inject({}) do |memo, item|
    parts = item.first.split('|').map(&:strip)
    memo[parts.first] = parts.slice(1,parts.size)
    memo
end

Gives you {"name1" => ["value1","value2","value3","value4"], "name2" => ["value1","value2","value3","value4"],...}

On a side note, whenever I write code like memo[parts.first] = parts.slice(1,parts.size) I really wish Ruby just added a head and tail method.

1 Comment

In Ruby, head is first and tail is drop(1), or get them both with head, *tail = list.

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.