8

Sometimes I need to deal with key / value data.

I dislike using Arrays, because they are not constrained in size (it's too easy to accidentally add more than 2 items, plus you end up needing to validate size later on). Furthermore, indexes of 0 and 1 become magic numbers and do a poor job of conveying meaning ("When I say 0, I really mean head...").

Hashes are also not appropriate, as it is possible to accidentally add an extra entry.

I wrote the following class to solve the problem:

class Pair
  attr_accessor :head, :tail

  def initialize(h, t)
      @head, @tail = h, t
  end
end

It works great and solves the problem, but I am curious to know: does the Ruby standard library comes with such a class already?

Rick
  • 8,366
  • 8
  • 47
  • 76

4 Answers4

4

No, Ruby doesn't have a standard Pair class.

You could take a look at "Using Tuples in Ruby?".

The solutions involve either using a similar class as yours, the Tuples gem or OpenStruct.

Python has tuple, but even Java doesn't have one: "A Java collection of value pairs? (tuples?)".

Community
  • 1
  • 1
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
3

You can also use OpenStruct datatype. Probably not exactly what you wanted, but here is an implementation ...

require 'ostruct'

foo = OpenStruct.new
foo.head = "cabeza"
foo.tail = "cola"

Finally,

puts foo.head
 => "cabeza"

puts foo.tail
 => "cola"
Som Poddar
  • 1,428
  • 1
  • 15
  • 22
2

No, there is no such class in the Ruby core library or standard libraries. It would be nice to have core library support (as well as literal syntax) for tuples, though.

I once experimented with a class very similar to yours, in order to replace the array that gets yielded by Hash#each with a pair. I found that monkey-patching Hash#each to return a pair instead of an array actually breaks surprisingly little code, provided that the pair class responds appropriately to to_a and to_ary:

class Pair
  attr_reader :first, :second

  def to_ary; [first, second] end
  alias_method :to_a, :to_ary

  private

  attr_writer :first, :second

  def initialize(first, second)
    self.first, self.second = first, second
  end

  class << self; alias_method :[], :new end
end
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
0

You don't need a special type, you can use a 2-element array with a little helper to give the pairs a consistent order. E.g.:

def pair(a, b)
  (a.hash < b.hash) ? [a, b] : [b, a]
end

distances = {
  pair("Los Angeles", "New York") => 2_789.6,
  pair("Los Angeles", "Sydney") => 7_497,
}

distances[ pair("Los Angeles", "New York") ]    # => 2789.6
distances[ pair("New York", "Los Angeles") ]    # => 2789.6
distances[ pair("Sydney", "Los Angeles") ]      # => 7497