रूबी में वस्तु विशेषता द्वारा यूनीक


126

एक या अधिक विशेषताओं के संबंध में एक सरणी में अद्वितीय वस्तुओं का चयन करने का सबसे सुंदर तरीका क्या है?

ये ऑब्जेक्ट ActiveRecord में संग्रहीत किए जाते हैं, इसलिए AR के तरीकों का उपयोग करना भी ठीक होगा।

जवाबों:


200

Array#uniqएक ब्लॉक के साथ प्रयोग करें :

@photos = @photos.uniq { |p| p.album_id }

5
रूबी 1.9 और बाद के संस्करणों के लिए यह सही उत्तर है ।
नुरेटिन

2
+1। और पहले के require 'backports'
रूबीज़ के लिए

हैश मेथड बेहतर है अगर आप एल्बम_ड बाय ग्रुप कहना चाहते हैं जबकि (कहते हैं) num_plays को समिट करें।
22

20
आप इसे to_proc ( ruby-doc.org/core-1.9.3/Symbol.html#method-i-to_proc ) के साथ सुधार सकते हैं :@photos.uniq &:album_id
joaomilho

रूबी 1.8 के लिए @brauliobo आपको बस इसी एसओ में नीचे पढ़ने की जरूरत है: stackoverflow.com/a/113770/213191
पीटर एच। बोलिंग

22

uniq_byअपनी परियोजना में सरणी को विधि में जोड़ें । यह के साथ सादृश्य द्वारा काम करता है sort_by। तो uniq_byजैसा है uniqवैसा sort_byहै sort। उपयोग:

uniq_array = my_array.uniq_by {|obj| obj.id}

कार्यान्वयन:

class Array
  def uniq_by(&blk)
    transforms = []
    self.select do |el|
      should_keep = !transforms.include?(t=blk[el])
      transforms << t
      should_keep
    end
  end
end

ध्यान दें कि यह आपके वर्तमान एक को संशोधित करने के बजाय एक नया सरणी देता है। हमने एक uniq_by!विधि नहीं लिखी है लेकिन यदि आप चाहते हैं तो यह काफी आसान होना चाहिए।

EDIT: ट्राइबलविब्स बताते हैं कि कार्यान्वयन O (n ^ 2) है। बेहतर होगा कुछ ऐसा (अप्रकाशित) ...

class Array
  def uniq_by(&blk)
    transforms = {}
    select do |el|
      t = blk[el]
      should_keep = !transforms[t]
      transforms[t] = true
      should_keep
    end
  end
end

1
नाइस एपीआई लेकिन यह खराब होने वाला है (ओ ^ (n ^ 2) जैसा दिखता है) बड़े सरणियों के लिए स्केलिंग प्रदर्शन। परिवर्तन को हैशसेट बनाकर तय किया जा सकता है।
आदिवासी

7
यह उत्तर पुराना है। रूबी> = 1.9 के पास एक ब्लॉक के साथ एरे # यूनीक है जो बिल्कुल ऐसा करता है, जैसा कि स्वीकृत उत्तर में है।
पीटर एच। बोलिंग

17

इसे डेटाबेस स्तर पर करें:

YourModel.find(:all, :group => "status")

1
और क्या होगा अगर यह एक से अधिक क्षेत्र, ब्याज से बाहर है?
रायन बिग

12

आप सरणी से कई विशेषताओं तत्वों द्वारा अद्वितीय का चयन करने के लिए इस चाल का उपयोग कर सकते हैं:

@photos = @photos.uniq { |p| [p.album_id, p.author_id] }

इतना स्पष्ट, तो रूबी। रूबी को आशीर्वाद देने का सिर्फ एक और कारण
ToTenMilan

6

मैंने मूल रूप से selectऐरे पर विधि का उपयोग करने का सुझाव दिया था । अर्थात:

[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0} हमें [2,4,6]वापस देता है ।

लेकिन अगर आप पहली ऐसी वस्तु चाहते हैं, तो उपयोग करें detect

[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}हमें देता है 4

मुझे यकीन नहीं है कि आप यहाँ क्या कर रहे हैं, हालांकि।


5

मैं विशिष्टता को लागू करने के लिए एक हैश के जेएमए के उपयोग को पसंद करता हूं। यहाँ एक और तरीका है कि बिल्ली को त्वचा के लिए कुछ और तरीके:

objs.inject({}) {|h,e| h[e.attr]=e; h}.values

यह एक अच्छा 1-लाइनर है, लेकिन मुझे संदेह है कि यह थोड़ा तेज हो सकता है:

h = {}
objs.each {|e| h[e.attr]=e}
h.values

3

यदि मैं आपके प्रश्न को सही ढंग से समझता हूं, तो मैंने मार्श किए गए ऑब्जेक्ट्स की तुलना करने के क्वैसी-हैकी दृष्टिकोण का उपयोग करके इस समस्या से निपटा है, यह निर्धारित करने के लिए कि क्या कोई विशेषता भिन्न है। निम्नलिखित कोड के अंत में इंजेक्शन एक उदाहरण होगा:

class Foo
  attr_accessor :foo, :bar, :baz

  def initialize(foo,bar,baz)
    @foo = foo
    @bar = bar
    @baz = baz
  end
end

objs = [Foo.new(1,2,3),Foo.new(1,2,3),Foo.new(2,3,4)]

# find objects that are uniq with respect to attributes
objs.inject([]) do |uniqs,obj|
  if uniqs.all? { |e| Marshal.dump(e) != Marshal.dump(obj) }
    uniqs << obj
  end
  uniqs
end

3

सबसे सुंदर तरीका जो मैंने पाया है वह Array#uniqएक ब्लॉक के साथ स्पिन-ऑफ का उपयोग करना है

enumerable_collection.uniq(&:property)

... यह बेहतर भी पढ़ता है!


2

आप हैश का उपयोग कर सकते हैं, जिसमें प्रत्येक कुंजी के लिए केवल एक मान होता है:

Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values



1

मुझे जेमाह और हेड के जवाब पसंद हैं। लेकिन क्या वे सरणी ऑर्डर को संरक्षित करते हैं? वे रूबी के बाद के संस्करणों में हो सकते हैं क्योंकि भाषा विनिर्देश में कुछ हैश प्रविष्टि-आदेश-संरक्षण आवश्यकताओं को लिखा गया है, लेकिन यहां एक समान समाधान है जो मुझे उपयोग करना पसंद है जो कि आदेश की परवाह किए बिना संरक्षित करता है।

h = Set.new
objs.select{|el| h.add?(el.attr)}

1

ActiveSupport कार्यान्वयन:

def uniq_by
  hash, array = {}, []
  each { |i| hash[yield(i)] ||= (array << i) }
  array
end

0

अब यदि आप विशेषता मानों को क्रमबद्ध कर सकते हैं तो यह किया जा सकता है:

class A
  attr_accessor :val
  def initialize(v); self.val = v; end
end

objs = [1,2,6,3,7,7,8,2,8].map{|i| A.new(i)}

objs.sort_by{|a| a.val}.inject([]) do |uniqs, a|
  uniqs << a if uniqs.empty? || a.val != uniqs.last.val
  uniqs
end

यह एक 1-विशेषता के लिए अद्वितीय है, लेकिन एक ही बात w / lexicographic तरह किया जा सकता है ...

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.