كيف يمكنني الحصول على العناصر الفريدة من مجموعة من التجزئات في روبي؟

StackOverflow https://stackoverflow.com/questions/181091

  •  05-07-2019
  •  | 
  •  

سؤال

ولدي مجموعة من التجزئة، وأريد القيم الفريدة للخروج منه. داعيا Array.uniq لا يعطيني ما أتوقع.

a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]

وأين كنت أتوقع:

[{:a => 1}, {:a => 2}]

في البحث حول على شبكة الإنترنت، لم أكن التوصل إلى الحل الذي كنت سعيدا معها. الناس أوصى بإعادة تعريف Hash.eql? وHash.hash، لأن ذلك هو ما Array.uniq والاستعلام.

وتحرير: حيث ركضت إلى هذا في العالم الحقيقي، وكانت علامات الرقم قليلا أكثر تعقيدا. وكانت نتيجة تحليل JSON التي كانت حقول متعددة، وبعضها كانت القيم التجزئة كذلك. كان لي مجموعة من تلك النتائج التي أردت أن تصفية القيم الفريدة.

وأنا لا أحب الحل Hash.eql? إعادة تعريف وHash.hash، لأنني إما أن تضطر إلى إعادة تعريف Hash عالميا، أو إعادة تعريفه لكل إدخال في بلدي مجموعة. أن تغيير تعريف Hash لكل إدخال تكون مرهقة، لا سيما وأن هناك قد تكون متداخلة التجزئة داخل كل دخول.

وتغيير Hash ديه بعض الإمكانات على مستوى العالم، وخاصة إذا تم القيام به بشكل مؤقت. كنت ترغب في بناء الطبقة أو المساعد دالة أخرى ملفوفة إنقاذ قبالة التعاريف القديمة، واستعادتها، ولكن أعتقد أن هذا يضيف المزيد من التعقيد مما هو مطلوب حقا.

وعن طريق inject يبدو وكأنه بديل جيد لإعادة Hash.

هل كانت مفيدة؟

المحلول

وأستطيع أن أحصل على ما أريد من خلال الدعوة inject

a = [{:a => 1},{:a => 2}, {:a => 1}]
a.inject([]) { |result,h| result << h unless result.include?(h); result }

وهذا سيعود:

[{:a=>1}, {:a=>2}]

نصائح أخرى

وروبي 1.8.7+ سيعود فقط ما لديك من المتوقع:

[{:a=>1}, {:a=>2}, {:a=>1}].uniq
#=> [{:a=>1}, {:a=>2}] 

ولقد كان وضعا مماثلا، ولكن كان تجزئات المفاتيح. اعتدت فرز الأسلوب.

وما أعنيه:

وكان لديك صفيف:

[{:x=>1},{:x=>2},{:x=>3},{:x=>2},{:x=>1}]

وقمت بفرز عليه (#sort_by {|t| t[:x]}) والحصول على هذا:

[{:x=>1}, {:x=>1}, {:x=>2}, {:x=>2}, {:x=>3}]

والآن بت معدلة من الإجابة من طرف Aaaron Hinni:

your_array.inject([]) do |result,item| 
  result << item if !result.last||result.last[:x]!=item[:x]
  result
end

ولقد حاول أيضا:

test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]}

ولكن كان بطيئا للغاية. هنا هو بلدي القياسي:

test=[]
1000.times {test<<{:x=>rand}}

Benchmark.bmbm do |bm|
  bm.report("sorting: ") do
    test.sort_by {|t| t[:x]}.inject([]) {|r,h| r<<h if !r.last||r.last[:x]!=h[:x]; r}
  end
  bm.report("inject: ") {test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]} }
end

والنتائج:

Rehearsal ---------------------------------------------
sorting:    0.010000   0.000000   0.010000 (  0.005633)
inject:     0.470000   0.140000   0.610000 (  0.621973)
------------------------------------ total: 0.620000sec

                user     system      total        real
sorting:    0.010000   0.000000   0.010000 (  0.003839)
inject:     0.480000   0.130000   0.610000 (  0.612438)

وعلى افتراض التجزئة الخاصة بك دائما واحدة أزواج قيمة المفتاح، هذا العمل:

a.map {|h| h.to_a[0]}.uniq.map {|k,v| {k => v}}

وHash.to_a يخلق مجموعة من المصفوفات قيمة المفتاح، وبالتالي فإن أول خريطة يحصل لك:

[[:a, 1], [:a, 2], [:a, 1]]

وUNIQ على صفائف يفعل ما تريد، مما يتيح لك:

[[:a, 1], [:a, 2]]

وبعد ذلك الخريطة الثانية يضعهم معا مرة أخرى كما التجزئة مرة أخرى.

ويمكنك استخدام (اختبار في روبي 1.9.3)،

[{a: 1},{a: 2},{a:1}].uniq => [{a:1},{a: 2}]
[{a: 1,b: 2},{a: 2, b: 2},{a: 1, b: 3}].uniq_by {|v| v[:a]} => [{a: 1,b: 2},{a: 2, b: 2}]

والجواب ان تعطي هي مماثلة لتلك التي نوقشت <لأ href = "http://mikeburnscoder.wordpress.com/2008/01/18/uniquify-an-array-of-hashes-in-ruby/" يختلط = "نوفولو noreferrer"> هنا . أنه يتجاوز hash وeql? الأساليب على التجزئات التي تظهر في مجموعة والتي ثم يجعل uniq تتصرف بشكل صحيح.

وطريقة الأنابيب على المصفوفات (متوفر منذ 1.8.6) يقوم الاتحاد مجموعة (عودة مجموعة)، لذلك وفيما يلي طريقة ممكنة أخرى للحصول على عناصر فريدة من نوعها من أي a مجموعة:

و[] | a

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top