tags:

views:

42

answers:

2

I would like to calculate the similarity between users, which is reciprocal.

similarity[:user1][:user2] == similarity[:user2][:user1]
So it would be great to use:
unordered_set(:user1, :user2) = 1
unordered_set(:user2, :user1) += 1

read_unordered_set(:user1, :user2) #=> 2
read_unordered_set(:user2, :user1) #=> 2

How can I get a similar behaviour in Ruby?

+6  A: 

http://www.ruby-doc.org/core/classes/Set.html

[1, 2].to_set == [2, 1].to_set

Might help...

Philip Brocoum
@collimarco: This answer works because Sets are unordered and do not contain duplicates by definition.
Peter Di Cecco
+1  A: 

The missing part here is a <=> operator on symbols. If you define one, then a solution would be:

# We define our own <=> operation on symbols
Symbol.class_eval do
  def <=>(other)
    self.to_s <=> other.to_s
  end
end


# Our set
class UnorderedSet
  def initialize
    @hash = Hash.new
  end

  def [](k1, k2)
    @hash[[k1, k2].sort]
  end

  def []=(k1, k2, value)
    @hash[[k1, k2].sort] = value
  end

  def keys
    @hash.keys
  end

  def values
    @hash.values
  end

  def each
    @hash.each do |k, v|
      yield(k, v)
    end
  end
  include Enumerable
end

Then of course we provide some tests for that container:

if __FILE__ == $0
  require 'test/unit'

  class UnorderedSetTest < Test::Unit::TestCase
    def setup
      @set = UnorderedSet.new
    end

    def test_bracket_operators
      assert_equal(nil, @set[:unknown, :key])

      @set[:user1, :user2] = 1
      @set[:user2, :user1] += 1

      assert_equal(2, @set[:user1, :user2])
      assert_equal(2, @set[:user2, :user1])
    end

    def test_enumerability
      h = {
        [:user1, :user2] => "ruby",
        [:c, :d] => "is",
        [:b, :a] => "easy",
        [:f, :e] => "!"
      }

      h.each do |k, v|
        @set[*k] = v
      end

      assert_equal(h.values.sort, @set.values.sort)
      assert_equal(h.keys.collect { |k| k.sort }.sort, @set.keys.sort)
      assert_equal(h.to_a.collect { |k, v| [k.sort, v] }.sort, @set.to_a.sort)
    end
  end
end

This code was tested with ruby 1.8.2. Of course, don't expect much performance-wise...

bltxd