简体   繁体   中英

Ruby: List all items in an array

I have tried at least 5 different ways to do it, currently using the .each method.

I believe the problem lies in the print_songs method at the bottom of the code block.

The error I'm getting:

Artist #print_songs lists all of the artist's songs
     Failure/Error: expect{artist.print_songs}.to output("Dirty Diana\nBillie Jean\n").to_stdout
       expected block to output "Dirty Diana\nBillie Jean\n" to stdout, but output "#<Song:0x000000019757b0>\n#<Song:0x000000019756e8>\n"
       Diff:
       @@ -1,3 +1,3 @@
       -Dirty Diana
       -Billie Jean
       +#<Song:0x000000019757b0>
       +#<Song:0x000000019756e8>

The code:

class Artist
  attr_accessor :name, :song
  @@all = []

  def initialize(name)
    @name = name
    @songs = []
  end

  def add_song(song)
    @songs << song
  end

  def songs
    @songs
  end

  def self.all
    @@all
  end

  def save
    self.class.all << self
  end

  def self.create_by_name(name)
    artist = Artist.new(name)
  end

  def self.find_or_create_by_name(name)
    artist_name = @@all.detect{|x| x.name == name}
    if artist_name == nil
      self.create_by_name(name)
    else
      artist_name
    end
  end

  def print_songs
    @songs.each{|song| puts song}
  end

end

Song Class:

class Song
  attr_accessor :name, :artist

  def initialize(name)
    @name = name
  end

  # def artist=(name)
  #   self.artist = Artist.new(name)
  # end

  def self.new_by_filename(file_name)
    file_name.basename("")
  end

end

Answer:

def print_songs
  @songs.each{|song| puts song.name}
end

It's apparent that the song is probably just not a string, but an object of some sort. Add a to_s method to whatever object that is and puts should automatically call that, although you could, of course, also call to_s manually.

Here's the quick test I did to confirm this behaviour

irb(main):001:0> class Song
irb(main):002:1> def initialize(title)
irb(main):003:2> @title = title
irb(main):004:2> end
irb(main):005:1> end
=> :initialize
irb(main):006:0> s = Song.new "scarborough fair"
=> #<Song:0x0000000030bb78 @title="scarborough fair">
irb(main):007:0> puts s
#<Song:0x0000000030bb78>
=> nil
irb(main):008:0> class Song
irb(main):009:1> def to_s
irb(main):010:2> return @title
irb(main):011:2> end
irb(main):012:1> end
=> :to_s
irb(main):013:0> puts s
scarborough fair
=> nil
irb(main):014:0>

EDIT:

But why is this?

In short, when you create some custom object class, like Song in my example above, Ruby has no idea how to represent it as a string. The default to_s method simply outputs the class and the object ID, which is OK for debugging, but if there's some obvious way to represent the object as a string, like in the case of Song (one would expect song.to_s to return the song title), one has to override the default to_s method or include / inherit from another class/module that has a more fitting implementation of to_s .

It is working as expected.Lets look at tried.Could you post the test cases you have tried with?

a1 = Artist.create(name: 'Lana del re')
a1.add_song('Young and Beautiful')
a1.add_song('haunted')
a1.print_songs
["Young and Beautiful", "haunted"] #output assuming song as a String object 

By default, the call to to_s prints the object's class name and an encoding of the object id like so: "#<Song:0x007f9fd16a0770>" . That said, you need to override the to_s method on a Song class:

class Song
  def initialize(title)
    @title = title
  end

  def to_s
    @title
  end
end

Then you would need to modify the print_songs method accordingly on an existing Artist class:

def print_songs
  puts @songs.each(&:to_s)
end

The each(&:to_s) bit basically invokes the to_s method on each Song object passed to the block, which is essentially the same as

def print_songs
  puts @songs.each { |song| song.to_s }
end

This could've also been rewritten as just

def print_songs
  puts @songs
end

and in this case puts would implicitly invoke the to_s method on the elements of @songs .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM