0

Let's say I have two objects: User and Race.

class User
  attr_accessor :first_name
  attr_accessor :last_name
end

class Race
  attr_accessor :course
  attr_accessor :start_time
  attr_accessor :end_time
end

Now let's say I create an array of hashes like this:

user_races = races.map{ |race| {:user => race.user, :race => race} }

How do I then convert user_races into an array of structs, keeping in mind that I want to be able to access the attributes of both user and race from the struct element? (The key thing is I want to create a new object via Struct so that I can access the combined attributes of User and Race. For example, UserRace.name, UserRace.start_time.)

RubyDubee
  • 2,426
  • 2
  • 23
  • 34
keruilin
  • 16,782
  • 34
  • 108
  • 175

4 Answers4

3

Try this:

class User
  attr_accessor :first_name
  attr_accessor :last_name
end

class Race
  attr_accessor :course
  attr_accessor :start_time
  attr_accessor :end_time
end

UserRace = Struct.new(:first_name, :last_name, :course, :start_time, :end_time)

def get_user_race_info      
  user_races = races.map do |r| 
    UserRace.new(r.user.first_name, r.user.last_name, 
              r.course, r.start_time, r.end_time)
  end
end

Now let's test the result:

user_races = get_user_race_info
user_races[0].first_name
user_races[0].end_time
Harish Shetty
  • 64,083
  • 21
  • 152
  • 198
  • If I try to put this as a method in the User class, like def self.fastest_time_record_holders races = Race.by_fastest_time UserRace = Struct.new(:first_name, :last_name, :course, :start_time, :end_time) user_races = races.map do |r| UserRace.new(r.user.first_name, r.user.last_name, r.course, r.start_time, r.end_time) end end I get a dynamic constant error. What do I need to do? – keruilin Mar 24 '10 at 16:19
  • You can't redefine the struct in your method. Put the struct definition out side the method body. I have updated my answer, please take a look – Harish Shetty Mar 24 '10 at 16:31
1

if your hash has so many attributes such that listing them all:

user_races = races.map{ |race| {:user => race.user, :race => race, :best_lap_time => 552.33, :total_race_time => 1586.11, :ambient_temperature => 26.3, :winning_position => 2, :number_of_competitors => 8, :price_of_tea_in_china => 0.38 } } # garbage to show a user_race hash with many attributes

becomes cumbersome (or if you may be adding more attributes later), you can use the * ("splat") operator.

the splat operator converts an array into an argument list. so you can populate Struct.new's argument list with the list of keys in your hash by doing:

UserRace = Struct.new(*races.first.keys)

of course, this assumes all hashes in your array have the same keys (in the same order).

once you have your struct defined, you can use inject to build the final array of objects. (inject greatly simplifies converting many objects from one data type to another.)

user_races.inject([]) { |result, user_race| result << UserRace.new(*user_race.values) }
Community
  • 1
  • 1
RUN-CMD
  • 340
  • 3
  • 12
1

Create a definition for the UserRace object (as a Struct), then just make an array of said objects.

UserRace = Struct.new(:user, :race)

user_races = races.map { |race| UserRace.new(race.user, race) }

# ...

user_races.each do |user_race|
  puts user_race.user
  puts user_race.race
end
Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • The question seems a little vague. But this seems right to me. – Levi Mar 24 '10 at 02:39
  • Yea, apologies if vague. The key thing is I want to create a new object via Struct so that I can access the combined attributes of User and Race. For example, UserRace.name, UserRace.start_time. Make sense? – keruilin Mar 24 '10 at 02:48
  • +1 - This is an elegant solution, but i think it doesn't answer the real question i.e it doesn't actually CONVERT array of hashes to array of structs... though it will solve this particular problem. – RubyDubee Mar 24 '10 at 10:07
0

You have this :::

user_races = races.map{ |race| {:user => race.user, :race => race} }

Now create a Struct as shown below :

UserRace = Struct.new(:user, :race)

And then ::

user_races.each do |user_race|
   new_array << UserRace.new(user_race[:user],user_race[:race])
end

Haven't tested the code... should be fine... what say?

EDIT: Here I am adding the objects of UserRace to a new_array.

RubyDubee
  • 2,426
  • 2
  • 23
  • 34