79

Does anyone use tuples in Ruby? If so, how may one implement a tuple? Ruby hashes are nice and work almost as well, but I'd really like to see something like the Tuple class in Python, where you can use . notation to find the value for which you are looking. I'm wanting this so that I can create an implementation of D, similar to Dee for Python.

SwiftMango
  • 15,092
  • 13
  • 71
  • 136
  • Did you ever implement D in Ruby? If so, do you have the link to it? I've been working on something similar recently, and I'd love to see what you've done so far. – dkubb Dec 15 '09 at 09:10
  • 1
    Nope; I never made it that far. I've been winding my way around several other things trying to get back to it. I did find several libraries that seem as though they would help: LazyList and arel. I basically came to the conclusion that LINQ in .NET was almost there, then found arel, which was also close. Using LazyList and removing the direct-to-SQL conversion, the latter of which is also a project goal, would almost get you there. That said, I would love to see what you have so far. I'm still a little way off from getting back to it. –  Dec 19 '09 at 05:32
  • ambition is another interesting looking library, but it hasn't been updated in some time. That was the one I found first. arel looks like it has continued in the same tradition. –  Dec 19 '09 at 05:57
  • I've just begun a project called veritas to work on this: http://github.com/dkubb/veritas It's still *really* early. If things go well I may update DataMapper to use it as a foundation. I'm not really interested in supporting Ambition-like syntax in the core, since you can only use ParseTree with 1.8, but I wouldn't have a problem with a plugin like the one I wrote for DataMapper: http://github.com/dkubb/dm-ambition – dkubb Jan 04 '10 at 07:41
  • I should add that I'm considering updating the underlying implementation to use Struct objects for the tuples to optimize memory usage, but at the moment I'm more concerned about correctness and speccing out the public API. – dkubb Jan 04 '10 at 07:43

7 Answers7

58

OpenStruct?

Brief example:

require 'ostruct'

person = OpenStruct.new
person.name    = "John Smith"
person.age     = 70
person.pension = 300

puts person.name     # -> "John Smith"
puts person.age      # -> 70
puts person.address  # -> nil
Andrew Backes
  • 1,884
  • 4
  • 21
  • 37
  • 2
    np :) to answer you question though: no i do not use tuples in ruby, as openstructs or otherwise. i use classes at the high end and hashes at the low end :) –  Feb 09 '09 at 11:01
  • 10
    this is a poor answer, just a link? Seriously? what happens if/when the link becomes broken? –  Oct 30 '13 at 22:26
  • 2
    it's not even the notion of Tuple you expose here. Just a data structure. Can't understand how your solution is the most upvoted – Jules Ivanic Dec 06 '16 at 15:39
  • 2
    @JulesIvanic valid comment but sometimes an OP does not know how to properly formulate a title for a question, in this case this is actually the answer that the OP is looking for :) – SidOfc Oct 16 '17 at 12:30
  • I know this question has been edited to have a proper answer, but I'd just like to acknowledge that the link is now broken. – Pieter-Jan Briers Jul 26 '23 at 13:58
36

Based on the fact that you talk about hashes and . notation I'm going to assume you mean a different kind of tuple than the (1. "a") sort. You're probably looking for the Struct class. eg:

Person = Struct.new(:name, :age)
me = Person.new
me.name = "Guy"
me.age =  30
Logan Capaldo
  • 39,555
  • 5
  • 63
  • 78
  • 1
    That's close, but having to name it bugs me. I was looking for something like the (1. "a") sort but with the property get/set notation you describe. –  Feb 08 '09 at 22:02
  • 6
    @panesofglass, there's no need to name nothing: a = Struct.new(:name, :age).new; a.name = "Guy" – paradoja Feb 09 '09 at 00:56
  • Can I set `a = Struct.new(:name, :age)` and later say a.new? I would suppose so. I'll have to check that out. It would be a lot more explicit as to what I want. –  Jul 26 '10 at 22:02
  • 5
    Finding this years later, I can confirm that `Person.new("Guy", 30)` does work in addition to setting the fields individually. – Greg Haskins Apr 29 '14 at 11:42
20

While this isn't strictly a tuple (can't do dot notation of members), you can assign a list of variables from a list, which often will solve issues with ruby being pass-by-value when you are after a list of return values.

E.g.

:linenum > (a,b,c) = [1,2,3]
:linenum > a
  => 1
:linenum > b
  => 2
:linenum > c
  => 3
e_m0ney
  • 978
  • 9
  • 14
15

Arrays are cool to use as tuples because of destructuring

a = [[1,2], [2,3], [3,4]]
a.map {|a,b| a+b }

Struct give you convenient . accessors

Person = Struct.new(:first_name, :last_name)
ppl = Person.new('John', 'Connor')
ppl.first_name 
ppl.last_name

You can get the convenience of both worlds with to_ary

Person = Struct.new(:first_name, :last_name) do
  def to_ary
    [first_name, last_name]
  end
end
# =>
[
  Person.new('John', 'Connor'), 
  Person.new('John', 'Conway')
].map { |a, b| a + ' ' + b  }
# => ["John Connor", "John Conway"]
Cyril Duchon-Doris
  • 12,964
  • 9
  • 77
  • 164
11

I'm the author of Gem for Ruby tuples.

You are provided with two classes:

  • Tuple in general
  • Pair in particular

You can initialize them in different ways:

Tuple.new(1, 2)
Tuple.new([1, 2])
Tuple(1, 2)
Tuple([1, 2])
Tuple[1, 2]

Both of the classes have some auxiliary methods:

  • length / arity - which returns number of values inside tuple
  • first / last / second (only pair) - which returns a corresponding elements
  • [] that gives you an access to a particular elements
Kamil Lelonek
  • 14,592
  • 14
  • 66
  • 90
  • What is the difference between all the initializers? Or are they all equivalent? (which I suppose) If known from functional programming, which would be an examplary language that uses this syntax? – sb813322 Jan 16 '23 at 12:10
8

You can mock the Scala tuples with this trick :

Tuple = Struct.new(:_1, :_2)

2.2.5 :003 > t = Tuple.new("a", "b")
 => #<struct Tuple _1="a", _2="b">
2.2.5 :004 > t._1
 => "a"
2.2.5 :005 > t._2
 => "b"

but here you can't have destructuring:

2.2.5 :012 > a, b = t
 => {:_1=>"a", :_2=>"b"}
2.2.5 :013 > a
 => {:_1=>"a", :_2=>"b"}
2.2.5 :014 > b
 => nil

But thanks to this trick : https://gist.github.com/stevecj/9ace6a70370f6d1a1511 destructuring will work:

2.2.5 :001 > Tuple = Struct.new(:_1, :_2)
 => Tuple
2.2.5 :002 > t = Tuple.new("a", "b")
 => #<struct Tuple _1="a", _2="b">
2.2.5 :003 > t._1
 => "a"
2.2.5 :004 > class Tuple ; def to_ary ; to_a ; end ; end
 => :to_ary
2.2.5 :005 > a, b = t
 => #<struct Tuple _1="a", _2="b">
2.2.5 :006 > a
 => "a"
2.2.5 :007 > b
 => "b"
Jules Ivanic
  • 1,579
  • 2
  • 15
  • 28
  • 1
    This could be made a little less confusing by simply defining the destructing accessor method in a block passed to `Struct.new` like https://ghostbin.com/paste/vwtg6 – dynsne Jun 10 '18 at 01:35
3

You can do something similiar with destructuring:

def something((a, b))
  a + b
end

p something([1, 2])

This prints out 3 as expected.

KARASZI István
  • 30,900
  • 8
  • 101
  • 128