Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-writing a "better" code

I am using Ruby 1.9 and I would like to know if there is a "better" way to write the following code.

  array_one = []
  array_two = []
  some_array.each { |value|
    array_one << value.id
    array_two << value.name
  }

I "don't like" to initialize array_one and array_two like in the above example and then add in those value.id and value.name. This because and mostly for a better understanding and reading of the code in my application.

I would like to provide the same functionality but with a "better" coding. Is it possible?

like image 767
user502052 Avatar asked Jun 18 '11 10:06

user502052


3 Answers

The code pattern you have there, is usually a sure sign of a fold (or more generally a catamorphism) wanting to get out. Ruby does provide a built-in method for folding, for historical reasons it is called inject.

Untested:

array_one, array_two = some_array.
  inject([[], []]) {|(array_one, array_two), value|
    [array_one << value.id, array_two << value.name]
}

Not sure whether you think this is "better" or not. For someone who understands what a fold is, this is probably clearer. For someone who doesn't, it's probably not.

(Although I think that if you don't understand what a fold is, then you are not a very good Ruby programmer, or even a very good programmer at all, but that is just my opinion.)

However, saying that some iteration is a fold isn't really terribly useful, since fold is a general method of iteration (which means that every form of iteration can be expressed as a fold). What you really have here, is a map:

array_one, array_two = some_array.map(&:id), some_array.map(&:value)

Note that this will traverse some_array twice. However, unless you have credible benchmarks and profiling results that show that this double traversal is a major source of performance troubles in your application, you really shouldn't worry about that.

like image 188
Jörg W Mittag Avatar answered Oct 22 '22 07:10

Jörg W Mittag


You could use the Array#collect method:

array_one = some_array.collect { |value| value.id }
array_two = some_array.collect { |value| value.name }

As Jörg W Mittag mentioned in his answer, this solution will also traverse the source array twice.

like image 25
joschi Avatar answered Oct 22 '22 06:10

joschi


You may want to use transpose to transform an n-element array containing two-element arrays into two n-element arrays:

array_one, array_two = some_array.map {|value| [value.id, value.name]}.transpose

Here's the RDoc link, though it's fairly terse.

like image 1
Andrew Grimm Avatar answered Oct 22 '22 05:10

Andrew Grimm