0

I have the following Ruby code, which calls the same function multiple times with different arguments, and pushes the results into a common array.

people_relations = []
people.zip(people_addresses).map do |person, address|
  people_relations.push(createRelation(person, address))
end
people.zip(people_ph_numbers).map do |person, phone_number|
  people_relations.push(createRelation(person, phone_number))
end
people.zip(people_aliases).map do |person, _alias|
  people_relations.push(createRelation(person, _alias))
end

def createRelation(e1, e2)
  [true, false].sample ? CurrentRelation.new(e1, e2) : PastRelation.new(e1, e2)
end

This code works just fine, but I feel like this is not the idiomatic Ruby way of doing things, and can be improved by compressing the code into less lines or made to look cleaner.

Is there a better way to write the code that appears above?

4
  • What's the meaning of the ternary with [true, false].sample? Commented Jul 23, 2018 at 1:42
  • @SebastianPalma It's my way of randomly choosing between creating a PastRelation or CurrentRelation object. Commented Jul 23, 2018 at 1:46
  • @SebastianPalma The definition of the function is already there, down the bottom. Commented Jul 23, 2018 at 1:54
  • Sorry, my bad... Commented Jul 23, 2018 at 1:55

2 Answers 2

2

You could create an array that contains all the people "attributes" you're going to use, and with Enumerable#each_with_object you can assign an initial array to fill with the result of each call to createRelation():

attributes = [people_addresses, people_ph_numbers, people_aliases]
relations = people.each_with_object([]).with_index do |(person, memo), index|
  attributes.each do |attribute|
    memo << createRelation(person, attribute[index])
  end
end
Sign up to request clarification or add additional context in comments.

1 Comment

this is very very very hard to read and understand, but contemplates the answer like a charm. Should be consider the correct answer.
1

I'd probably go with a transpose -> flat_map solution for this myself, for instance given:

def CreateRelation(person, relationship)
  if [true, false].sample
    "#{person} is currently related to #{relationship}"
  else
    "#{person} used to be related to #{relationship}"
  end
end

people = ['Person 1', 'Person 2', 'Person 3']
addresses = ['Person 1 Address', 'Person 2 Address', 'Person 3 Address']
phone_numbers = ['Person 1 Phone', 'Person 2 Phone', 'Person 3 Phone']
aliases = ['Person 1 AKA', 'Person 2 AKA', 'Person 3 AKA']

We can stick those 4 arrays into a single array and then transpose them, so the first element of each ends up in an array with each other, the second in another, and the last in a third:

[people, addresses, phone_numbers, aliases].transpose # => [
#  ["Person 1", "Person 1 Address", "Person 1 Phone", "Person 1 AKA"],
#  ["Person 2", "Person 2 Address", "Person 2 Phone", "Person 2 AKA"],
#  ["Person 3", "Person 3 Address", "Person 3 Phone", "Person 3 AKA"]]

and then you can flat_map those by calling CreateRelation:

result = [people, addresses, phone_numbers, aliases].transpose.flat_map do |person, *relations|
  relations.map { |relationship| CreateRelation(person, relationship) }
end
#["Person 1 used to be related to Person 1 Address",
# "Person 1 used to be related to Person 1 Phone",
# "Person 1 used to be related to Person 1 AKA",
# "Person 2 is currently related to Person 2 Address",
# "Person 2 used to be related to Person 2 Phone",
# "Person 2 is currently related to Person 2 AKA",
# "Person 3 is currently related to Person 3 Address",
# "Person 3 used to be related to Person 3 Phone",
# "Person 3 used to be related to Person 3 AKA"]

Or, at that point you could stick with just iterating and pushing, if you don't want to map/flat_map.


The more I think about it, the more I think I'd go with transpose -> each_with_object, instead of flat_map...less "create an array and then throw it away", I'll leave this with flat_map though because it is another option and @Sebastian Palma has each_with_object covered.

2 Comments

I liked this one most because it is the most concise and "artistic" solution. Thanks!
@JavascriptLoser As an aside since I have no overt objections to this answer Array#zip can accept any number of Arrays so people.zip(addresses, phone_numbers, aliases) would have the same result as [people, addresses, phone_numbers, aliases].transpose

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.