massaskillz massaskillz - 7 months ago 11
Ruby Question

Creating new arrays from existing arrays in Ruby

I have the following 2 arrays:

fields = ["name", "team", "number", "name", "team", "number", "name", "team", "number"]
values = ["Patrick Ewing", "New York Knicks", 33, "Rik Smits", "Indiana Pacers", 45, "Bill Russell", "Boston Celtics", 6]


The fields will always be only name, team and number, but the number of values (i.e., number of players) will vary each time.

I want to create new arrays for each field type, so that I will get the following:

names = ["Patrick Ewing", "Rik Smits", "Bill Russell"]
teams = ["New York Knicks", "Indiana Pacers", "Boston Celtics"]
numbers = [33,45,6]


What's a good way to do this? I have tried the following, but want to know if there are other solutions that would perform better with larger arrays (up to 300). Or would the difference be negligible?

names = values.values_at(*(fields.each_index.select{ |i| fields[i] == "name"}))
teams = values.values_at(*(fields.each_index.select{ |i| fields[i] == "team"}))
numbers = values.values_at(*(fields.each_index.select{ |i| fields[i] == "number"}))

vee vee
Answer

Here is another/concise way:

> values = ["Patrick Ewing", "New York Knicks", 33, "Rik Smits", "Indiana Pacers", 45, "Bill Russell", "Boston Celtics", 6]
> name, team, number = values.each_slice(3).each_with_object([]) { |_, acc| acc << _ }.transpose
=> name
> ["Patrick Ewing", "Rik Smits", "Bill Russell"]
=> team
> ["New York Knicks", "Indiana Pacers", "Boston Celtics"]
=> number
> [33, 45, 6]

I liked @tadman's answer but didn't want to introduce extra variables hence the use of each_with_object. The beauty of Ruby Array#transpose comes into play, particulary for your problem.

Comments