Iterating over arrays in lockstep

(David Brady) #1

Hi,

James Gray's refactoring of my decidedly anidiomatic solution to Quiz #43 has caused me to add a note to my "Ruby Way journal" that basically says, "don't use Array indices if you can avoid it". My C++/Python experience leads me reflexively to indices even when iteratiors and append methods like << provide a more elegant solution.

Today I met a coding problem that made me start to reach for indices, and since the above note is recent in my journal, I stopped to think how it might be done without them. Nothing's coming to mind so I thought I'd post here and see if there's yet another Ruby Epiphany waiting to happen to me. :slight_smile:

I want to print a list of items and the output of a transform on those items. E.g. each element should be something like "#{x}: #{f(x)}". The transform is applied to the entire array at once, however, and returns an entire array, I can't just iterate on the array applying f(x) to each element. (The transform normalizes an array, returning an array of ratios of item / (sum of all items).)

My original thought was to simply say:

normalized_values = values.normalize

...but now values and normalized_values are two arrays that are implicitly related by index. Better, I think, somehow to relate them explicitly.

Normalize is a method I have added to Array. One way to explicitly relate the methods would be to add an iterator method to Array:

class Array
  def each_normal
    normals = self.normalize
    normals.each do |n|
      yield n
    end
  end
end

...but that still doesn't feel right. each_normal makes me think that I have a subclass struggling to get out here: I want to add a method "normal(n)" that returns the normal of item at index n, and doing that means that the Array really should be subclassed so it can cache a @total value for performance reasons. Array probably *should* be subclassed anyway, since normalize really only applies to Arrays of things that can be summed and divided.

But I've already started another thread about that. Right now, I'm just thinking that when you find yourself walking two arrays in parallel, that seems to me to be a code smell.

What's the cleanup? Zipping the two arrays together would even be a better solution, I think, thought it makes for uglier code like "#{pair[0]}: #{pair[1]}".

Thoughts?

-dB

···

--
David Brady
ruby_talk@shinybit.com
I'm feeling really surreal today... OR AM I?

(Levin Alexander) #2

zip takes a block, so you could just do:

   array.zip(array.normalize) { |item, normalized_item| "..." }

-Levin

···

David Brady <ruby_talk@shinybit.com> wrote:

What's the cleanup? Zipping the two arrays together would even be a better solution, I think, thought it makes for uglier code like "#{pair[0]}: #{pair[1]}".