Question

I am attempting to output a 2D array to the console; I want the information in the array to be nicely formatted as shown in my desired output at the end of this question. My array is created as follows (instances are created of the FamilyMember class):

#family_members.rb

class FamilyMember

attr_accessor :name, :sex, :status, :age
def initialize (name, sex, type, role, age)
   @name = name
   @sex = sex
   @type = type
   @role = role
   @age = age
end

end

# Below, an array is created called fm; instances of the class are then instantiated within the array elements
fm = {}
fm[1] = FamilyMember.new('Andrew','Male', 'Child', 'Son' , '27' )
fm[2] = FamilyMember.new('Bill','Male', 'Parent', 'Father' , '63' )
fm[3] = FamilyMember.new('Samantha','Female', 'Parent', 'Mother' , '62' )
fm[4] = FamilyMember.new('Thomas','Male', 'Child', 'Dog' , '10' )
fm[5] = FamilyMember.new('Samantha', 'Female', 'Child', 'Dog' , '4' )

I want to be able to output the contents of the array to the console formatted as a string. I need to be able to do this two ways - using each and seperately by using do.

What I have attempted (inspired by a previous SO question):

def eacharray(an_array)
  an_array.each do |inner|
      puts inner.join(" ")
  end
end

eacharray(fm)

However the output from the above is as follows:

1 #<FamilyMember:0x000000027e7d48>
2 #<FamilyMember:0x000000027e7c58>
3 #<FamilyMember:0x000000027e7b68>
4 #<FamilyMember:0x000000027e7a78>
5 #<FamilyMember:0x000000027e7988>

How do I output the 2D array elements nicely formatted using each and do?. Any help appreciated. Thanks.

Ideally, my output would be like this:

Family Member   Name     Sex     Type    Role    Age
1               Andrew   Male    Child   Son     27
2               Bill     Male    Parent  Father  63
3               Samantha Female  Parent  Mother  62 
4               Thomas   Male    Child   Dog     10
5               Samantha Female  Child   Dog     4 
Was it helpful?

Solution

If you're happy with a fixed format (you could build the format dynamically depending on the maximum data width in each field) you could write something like this.

class FamilyMember
  attr_accessor :name, :sex, :type, :role, :age
  def initialize (*args)
    @name, @sex, @type, @role, @age = args
  end
end

fm = []
fm << FamilyMember.new( 'Andrew',   'Male',   'Child',  'Son' ,    '27' )
fm << FamilyMember.new( 'Bill',     'Male',   'Parent', 'Father',  '63' )
fm << FamilyMember.new( 'Samantha', 'Female', 'Parent', 'Mother',  '62' )
fm << FamilyMember.new( 'Thomas',    'Male',  'Child',  'Dog' ,    '10' )
fm << FamilyMember.new( 'Samantha', 'Female', 'Child',  'Dog' ,     '4' )

format = '%-15s %-8s %-7s %-7s %-7s %s'
puts format % ['Family Member', 'Name', 'Sex', 'Type', 'Role', 'Age']
fm.each_with_index do |member, i|
  puts format % [ i+1, member.name, member.sex, member.type, member.role, member.age ]
end

output

Family Member   Name     Sex     Type    Role    Age
1               Andrew   Male    Child   Son     27
2               Bill     Male    Parent  Father  63
3               Samantha Female  Parent  Mother  62
4               Thomas   Male    Child   Dog     10
5               Samantha Female  Child   Dog     4

You can also use for ... in, which actually compiles to pretty much the same loop, using the each iterator.

i = 0
for member in fm do
  i += 1
  puts format % [ i, member.name, member.sex, member.type, member.role, member.age ]
end

or you can use the primitive while or until loop constructs, which most Ruby programmers forget about. Ruby is much more expressive using its iterators.

i = 0
while member = fm[i] do
  i += 1
  puts format % [ i, member.name, member.sex, member.type, member.role, member.age ]
end

Note that you can omit the do from both of these last examples. As long as you have a newline (or a semicolon) after the while expression Ruby will understand just fine.

OTHER TIPS

A class like your FamilyMember is most easily constructed with a Struct. The result is just a class, but with some extra features.

FamilyMember = Struct.new(:name, :sex, :type, :status, :age)
fm = []
fm << FamilyMember.new('Andrew','Male', 'Child', 'Son' , '27' )
fm << FamilyMember.new('Bill','Male', 'Parent', 'Father' , '63' )

puts FamilyMember.members.map(&:capitalize).join("\t") #members gives you the names of all methods
fm.each{|fam_member|puts fam_member.values.join("\t")} #another feature.

Output:

 Name   Sex Type    Status  Age
 Andrew Male    Child   Son 27
 Bill   Male    Parent  Father  63

Only lined out...

I understand that, This may be not relevant now. But Just sharing this so someone can take advantage.

Try This Gem - terminal-table

terminal-table

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top