تمديد طريقة UNIQ
-
21-09-2019 - |
سؤال
هذا هو سؤال روبي 1.8:
كلنا نعرف كيف نستخدم Array#uniq
:
[1,2,3,1].uniq #=> [1,2,3]
ومع ذلك ، أتساءل عما إذا كان بإمكاننا قرد تصحيحها بطريقة للعمل مع كائنات معقدة. السلوك الحالي مثل هذا:
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq
#=> [{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}]
المطلوب هو:
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq
#=> [{"three"=>"3"}, {"three"=>"4"}]
المحلول
إنه يعمل بالفعل بالنسبة لي في 1.8.7.
1:~$ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]
1:~$ irb -v
irb 0.9.5(05/04/13)
1:~$ irb
>> [{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq
=> [{"three"=>"3"}, {"three"=>"4"}]
نصائح أخرى
لجعل Array#Uniq يعمل من أجل أي كائن يجب عليك تجاوز طريقتين: hash و eql؟
تحتوي جميع الكائنات على طريقة التجزئة التي تحسب قيمة التجزئة لهذا الكائن ، لذا فإن كائنين يساويان قيمهما عندما يكون التجزئة متساوية أيضًا.
مثال-المستخدم فريد من نوعه عندما يكون عنوان بريده الإلكتروني فريدًا:
class User
attr_accessor :name,:email
def hash
@email.hash
end
def eql?(o)
@email == o.email
end
end
>> [User.new('Erin Smith','roo@example.com'),User.new('E. Smith','roo@example.com')].uniq
=> [#<User:0x1015a97e8 @name="Erin Smith", @email="maynurd@example.com"]
المشكلة هي Hash#hash
و Hash#eql?
كلاهما يعطي نتائج وهمية في Ruby 1.8.6. هذه واحدة من بقع Monkey النادرة جدًا التي كنت على استعداد لأداءها ، لأن هذا الخطأ يكسر بشكل خطير الكثير من التعليمات البرمجية - ولا سيما وظائف المذكرات. فقط كن حذرًا مع بقع القرد التي لا تتغلب عليها السلوك غير المنكسر.
لذا:
class Hash
if {}.hash != {}.hash
def hash
# code goes here
end
end
if !{}.eql?({})
def eql?(other)
# code goes here
end
end
end
ولكن إذا كنت تفعل شيئًا حيث يمكنك التحكم في بيئة النشر ، فما عليك سوى رفع خطأ إذا بدأ التطبيق بـ 1.8.6.
وماذا عن هذا؟
h={}
[{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].select {|e| need=!h.key?(e) ; h[e]=1 ; need}
#=> [{"three"=>"3"}, {"three"=>"4"}]
لقد واجهت هذا بنفسي عدة مرات. تجزئة المساواة في Ruby 1.8.6 مكسورة:
require 'test/unit'
class TestHashEquality < Test::Unit::TestCase
def test_that_an_empty_Hash_is_equal_to_another_empty_Hash
assert({}.eql?({}), 'Empty Hashes should be eql.')
end
end
يمر في Ruby 1.9 و Ruby 1.8.7 ، فشل في Ruby 1.8.6.
1.8.7 :039 > [{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq {|x|x.values}
=> [{"three"=>"3"}, {"three"=>"4"}]
1.8.7 :040 > [{"three"=>"3"}, {"three"=>"4"}, {"three"=>"3"}].uniq {|x|x.keys}
=> [{"three"=>"3"}]
ماذا عن شيء من هذا القبيل؟ فقط uniq_by قيمة التجزئة أو مفتاح التجزئة عبر الكتلة.