テストの時
assert [1,2,3].fuzzy_equal?(User.find_by_school("hoge").map(&:id))
とかやりたい。eachで比較するのが面倒だし、==メソッドは順序まであってないとfalseになるから。
実装
- バグあります
def fuzzy_equal?(other) original_size = self.size size = (self | other).size original_size == size end
で和集合をとってきても元とサイズが同じなら2つは同じ要素からなる。 |
全然だめだった
- 反例
[1,1].fuzzy_equal?([1,2]) #=> true [1,2,3].fuzzy_equal?([1]) #=> true
うーん。同じ要素のこととか考えると、uniqしてなんかしようかな。でも、そうすると、同じ要素が1つになってしまうから、[1]と[1,1]が同じになる。
include?使っても同じ要素が面倒だしなー。
あ、ソートかけて==すればいいのか。
sort使う
これもバグあります。
def fuzzy_equal?(other) self.sort_by{|e| e} == other.sort_by{|e| e} end
要素にnilが含まれている時にあっけなくエラー。
nilを含むソートと、Enumerable#partitionメソッド - @luke_silvia.diaryで使ったpartitionを使うことにした。
最終的にこうなりました
class Array def fuzzy_equal?(other) self.sort_by_element_with_nil == other.sort_by_element_with_nil end protected def sort_by_element_with_nil self_not_nil,self_nil = partition_by_nil self_sorted = self_not_nil.sort_by_element self_sorted + self_nil end def partition_by_nil partition{|e| e} end def sort_by_element sort_by{|e| e} end end if $0 == __FILE__ require 'test/unit' class TestArrayExt < Test::Unit::TestCase def test_fuzzy_equal arr = [1,2,3,nil] _true_ = [[1,2,3,nil],[3,2,nil,1],[nil,1,3,2]] _false_ = [[1],[1,2],[1,1,2,3],[1,2,3,3],[1,2,3,nil,nil]] _true_.each{|other| assert arr.fuzzy_equal?(other), other.inspect} _false_.each{|other| assert !arr.fuzzy_equal?(other), other.inspect} end end end