0

I have an array of hashes in the format

albums = [ {name: "Sgt. Pepper's Lonely Hearts Club Band", year: "1967" }, 
           { name: "Are You Experienced?", year: "1967" } 
         ]

etc..

I am trying to insert this output into a string that then needs to be inserted into html. I currently have this

def convert_to_html albums
  string_before = 
    "<html>
      <head>
        <title>\"Rolling Stone's ....\"</title>
      </head>
      <body>
        <table>\n" + 
        albums.each do |x| 
          "name is: #{x.values[0]} year is: #{x.values[1]}"
        end  + "
        </table>
      </body>
    </html>"
end

I know that this probably isn't the best way to get the values but I can't figure out any other way, but my major problem is that when I try this I get this error:

[2013-01-27 00:16:20] ERROR TypeError: can't convert Array into String
    albums.rb:72:in `+'
    albums.rb:72:in `convert_to_html'
    albums.rb:28:in `block in render_list'
    albums.rb:26:in `open'
    albums.rb:26:in `render_list'
    albums.rb:9:in `call'

Is there any way to insert the values from each hash side by side into the string?

P.S. I used name and year for readability. I know #{x.values[0]} #{x.values[1]} is the correct way to format this.

0

3 Answers 3

2

In Ruby, String#+ does not implicitly convert the right operand to a string.

# like post - oops!
> "hello " + ["world", "moon"]
=> #<TypeError: can't convert Array into String>

# valid, but .. ugly
> "hello " + ["world", "moon"].to_s
=> "hello [\"world\",\"moon\"]"

# likely desired
> "hello " + ["world", "moon"].join(" and ")
=> "hello world and moon"

Also, in the post albums.each .. do returns the initial array (albums) object, but that is still wrong as the result of the block is discarded (use Array.each for performing side-effects). Instead, use albums.map .. do (or albums.collect .. do) to collect/use the block results.

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

Comments

0

This is simple code showing how to do it:

album_list = [
  {name: "Sgt. Pepper's Lonely Hearts Club Band", year: "1967" }, 
  { name: "Are You Experienced?", year: "1967" } 
]

def convert_to_html(albums)

"<html>
  <head>
    <title>\"Rolling Stone's ....\"</title>
  </head>
  <body>
    <table>
" + albums.map { |album| 
"     <tr><td>name is: #{ album[:name] } year is: #{ album[:year] }</td></tr>"
}.join("\n") +
"
    </table>
  </body>
</html>"

end

puts convert_to_html(album_list)

Which outputs:

<html>
  <head>
    <title>"Rolling Stone's ...."</title>
  </head>
  <body>
    <table>
    <tr><td>name is: Sgt. Pepper's Lonely Hearts Club Band year is: 1967</td></tr>
    <tr><td>name is: Are You Experienced? year is: 1967</td></tr>
    </table>
  </body>
</html>

In real life I'd use ERB or HAML to generate the HTML. They're great templating tools.

Other ways of writing:

<tr><td>name is: #{ album[:name] } year is: #{ album[:year] }</td></tr>"

Are:

<tr><td>name is: %s year is: %s </td></tr>" % [ album[:name], album[:year] ] 

or:

<tr><td>name is: %s year is: %s </td></tr>" % album.values_at(:name, :year)

I don't recommend using album.values by itself because it relies on the order of insertion into the album hash. In the future, if you modify the hash by putting something into it in a different order while maintaining the code, the values order will change, breaking it in a way that might not be obvious.

Instead, use one of these ways of accessing the values to always explicitely get the value associated with the key. That's part of programming defensively.

1 Comment

The reason the poster's solution fails is due to the fact he is using a .each within the string like that in the first place. .each operates like a for loop, it is not returning new data like the .map method you have used here.
0

albums.each is not doing what you think it is. I get the impression that you want it to append something along these lines into your string.

name is: Sgt. Pepper's Lonely Hearts Club Band year is: 1967 name is: Are you Experienced? year is: 1967

What actually happens is effectively nothing. You have an expression inside a .each which is the same as a for loop that is simultaneously trying to append the albums array into your string. Hence the can't convert Array into String error.

The easiest way to achieve what you want would be to get the string up front and then append it into your string. Something akin to this.

def convert_to_html albums
append_string = ""
albums.each do |album|
  append_string += "<tr><td>name is: #{album[:name]} year is #{album[:year]} </td></tr>"
end

string_before =
  "<html>
    <head>
      <title>\"Rolling Stone's ....\"</title>
    </head>
    <body>
      <table>\n" + append_string + "
      </table>
    </body>
  </html>"
end

5 Comments

This won't generate valid HTML. You're missing the <tr> and <td> tags.
Nor did the original answer. I was addressing his question at hand. I have modified my answer to address this though now.
I tried this, with the #{album[:name]} it gave me a '"cannot convert Symbol to String"' error no matter what I did.
Which ruby build are you using? I just ran it on my machine without errors against ruby-1.9.3-p194.
hmm. now it's working so I don't know what I was doing differently.

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.