2012-06-30 13 views
38

में एक सरणी के भीतर वस्तुओं के गुणों योग करने के लिए कैसे मैं समझता हूँ कि आदेश, रूबी एक में सरणी तत्वों योग करने के लिए सुई विधि का उपयोग कर सकते हैं यानीरूबी

array = [1,2,3,4,5]; 
puts array.inject(0, &:+) 

लेकिन मैं कैसे वस्तुओं के गुणों का योग है एक ऑब्जेक्ट सरणी के भीतर उदाहरण के लिए?

वस्तुओं की एक सरणी है और प्रत्येक ऑब्जेक्ट में संपत्ति "नकद" है। इसलिए मैं अपने नकदी शेष को कुल मिलाकर जोड़ना चाहता हूं। कुछ की तरह ...

array.cash.inject(0, &:+) # (but this doesn't work) 

मुझे पता है मैं शायद ही संपत्ति नकदी से बना एक नई सरणी बनाने के लिए और इस योग सकता है, लेकिन मैं यदि संभव हो तो एक क्लीनर विधि के लिए देख रहा हूँ!

उत्तर

50
array.map(&:cash).inject(0, &:+) 

या

array.inject(0){|sum,e| sum + e.cash } 
+0

परफेक्ट धन्यवाद इंजेक्षन देख सकते हैं! –

+3

यह दो बार 'सरणी' पर चला जाता है, हालांकि बहुत सारे तत्व होने पर सलाह नहीं दी जा सकती है।क्यों न केवल इंजेक्शन के लिए उचित ब्लॉक का उपयोग करें? इसके अलावा 'कम/इंजेक्ट' सीधे प्रतीक तर्क लेता है, 'प्रतीक # to_proc' की आवश्यकता नहीं :-) –

+0

ध्यान दें कि आपको ब्लॉक भेजने की आवश्यकता नहीं है,' इंजेक्ट 'जानता है कि प्रतीक के साथ क्या करना है: 'इंजेक्ट करें (0,: +) ' – tokland

8

#reduce एक ब्लॉक लेता है (&:+ कि + करता है एक proc/ब्लॉक बनाने के लिए एक शॉर्टकट है)। ऐसा करने का एक ही रास्ता है कि आप क्या चाहते है:

array.reduce(0) { |sum, obj| sum + obj.cash } 
+2

' # less' '# इंजेक्ट' के लिए 1.9+, btw में उपनाम है। – Theo

+0

+1 'सरणी' पर दोहराए जाने के लिए +1 नहीं। उपनाम भी 1.8.7 बीटीडब्ल्यू में है। –

+1

जैसा कि माइकल का कहना है कि यह अधिक जगह-कुशल है कि मानचित्र + कम हो, लेकिन मॉड्यूलरिटी की लागत पर (इस मामले में छोटा, कहने की कोई आवश्यकता नहीं है)। रूबी 2.0 में हम दोनों आलस्य के लिए धन्यवाद कर सकते हैं: 'array.lazy.map (&: नकद) .reduce (0,: +)'। – tokland

1

इंजेक्षन में प्रारंभिक उपयोग करने के लिए कोई ज़रूरत नहीं है और साथ ही आपरेशन किया जा सकता है कम

array.map(&:cash).inject(:+) 
+3

आप प्रतीक तर्क के बारे में सही हैं, लेकिन यदि 'सरणी' खाली हो सकती है, तो आप तर्क चाहते हैं: '[] .inject (: +) # => nil',' [] .inject (0,: +) # => 0' जब तक आप अलग से 'शून्य' से निपटना नहीं चाहते हैं। –

+0

अच्छा बिंदु, इसके बारे में नहीं सोचा था। – megas

36

तुम भी कोशिश कर सकते हैं:

array.sum(&:cash)

इंजेक्ट व्यवसाय के लिए यह एक शॉर्टकट है और मुझे और अधिक पठनीय लगता है।
http://api.rubyonrails.org/classes/Enumerable.html

+3

यदि आप रेल का उपयोग कर रहे हैं, तो यह रास्ता तय करने का तरीका है। – Dennis

+0

ध्यान दें कि यदि आपकी सरणी ActiveRecord ऑब्जेक्ट पर किसी प्रकार की फ़िल्टरिंग का परिणाम है, उदा। '@orders = Order.all; @ आदेश। चयन {| ओ | o.status == 'paid'} .sum (&: cost) ', तो आप एक ही प्रश्न के साथ एक ही परिणाम प्राप्त कर सकते हैं:' @ order.where (स्थिति:: भुगतान) .sum (: लागत) '। – Dennis

+0

यदि रिकॉर्ड डीबी में संग्रहीत नहीं हैं, तो योग 0 होगा, जहां इंजेक्ट काम करेगा। – dgmora

2

अधिकांश संक्षिप्त तरीके:

array.map(&:cash).sum 

नक्शे से उत्पन्न सरणी शून्य आइटम हों तो:

array.map(&:cash).compact.sum 
0

तो योग के लिए शुरू मान 0 है, तो केवल योग समान है इंजेक्ट करने के लिए:

array.map(&:cash).sum 

और मैं ब्लॉक संस्करण को पसंद करूंगा:

array.sum { |a| a.cash } 

क्योंकि प्रतीक से प्रक्रिया अक्सर सीमित होती है (कोई पैरामीटर इत्यादि नहीं)।

(जरूरत Active_Support)

0

यहाँ कुछ दिलचस्प मानक

array = Array.new(1000) { OpenStruct.new(property: rand(1000)) } 

Benchmark.ips do |x| 
    x.report('map.sum') { array.map(&:property).sum } 
    x.report('inject(0)') { array.inject(0) { |sum, x| sum + x.property } } 
    x.compare! 
end 

और परिणाम

Calculating ------------------------------------- 
      map.sum 249.000 i/100ms 
      inject(0) 268.000 i/100ms 
------------------------------------------------- 
      map.sum  2.947k (± 5.1%) i/s -  14.691k 
      inject(0)  3.089k (± 5.4%) i/s -  15.544k 

Comparison: 
      inject(0):  3088.9 i/s 
      map.sum:  2947.5 i/s - 1.05x slower 

आप एक छोटा सा तेजी से