रूबी सरणी में समान स्ट्रिंग तत्वों की गणना कैसे करें


92

मेरे पास निम्नलिखित है Array = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

मैं प्रत्येक समान तत्व के लिए एक गिनती कैसे उत्पन्न करूं ?

Where:
"Jason" = 2, "Judah" = 3, "Allison" = 1, "Teresa" = 1, "Michelle" = 1?

या हैश का उत्पादन कहां करें:

कहां: हैश = {"जेसन" => 2, "जुदा" => 3, "एलीसन" => 1, "टेरेसा" => 1, "मिशेल" => 1}


3
रूबी 2.7 के रूप में आप उपयोग कर सकते हैं Enumerable#tally। अधिक जानकारी यहाँ
SRack

जवाबों:



128
names.inject(Hash.new(0)) { |total, e| total[e] += 1 ;total}

आपको देता है

{"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1} 

3
+1 चयनित उत्तर की तरह, लेकिन मैं इंजेक्शन का उपयोग पसंद करता हूं और कोई "बाहरी" चर नहीं।

18
यदि आप के each_with_objectबजाय आप का उपयोग injectकरते हैं तो आपको ;totalब्लॉक पर वापस नहीं लौटना होगा ।
mfilej

13
पोस्टीरिटी के लिए, यह @mfilej का अर्थ है:array.each_with_object(Hash.new(0)){|string, hash| hash[string] += 1}
गोन जिफ्रोनी

2
रूबी 2.7 से, आप बस कर सकते हैं names.tally:।
हॉलगीर विल्हेमसेन

104

रूबी v2.7 + (नवीनतम)

रूबी v2.7.0 (दिसंबर 2019 को जारी) के रूप में, मुख्य भाषा में अब शामिल है Enumerable#tally- एक नई विधि , विशेष रूप से इस समस्या के लिए डिज़ाइन की गई:

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

names.tally
#=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

रूबी v2.4 + (वर्तमान में समर्थित, लेकिन पुराने)

निम्न कोड मानक रूबी में संभव नहीं था जब यह प्रश्न पहली बार (फरवरी 2011) पूछा गया था, क्योंकि यह उपयोग करता है:

  • Object#itself, जो रूबी v2.2.0 (दिसंबर 2014 को जारी) में जोड़ा गया था।
  • Hash#transform_values, जो रूबी v2.4.0 (दिसंबर 2016 को जारी) में जोड़ा गया था।

रूबी के लिए ये आधुनिक जोड़ निम्नलिखित कार्यान्वयन को सक्षम करते हैं:

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

names.group_by(&:itself).transform_values(&:count)
#=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

रूबी v2.2 + (पदावनत)

यदि पुराने माणिक संस्करण का उपयोग करते हुए, उपर्युक्त Hash#transform_valuesविधि तक पहुंच के बिना , आप इसके बजाय उपयोग कर सकते हैं Array#to_h, जो रूबी v2.1.0 (दिसंबर 2013 को जारी) में जोड़ा गया था:

names.group_by(&:itself).map { |k,v| [k, v.length] }.to_h
#=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

यहां तक ​​कि पुराने रूबी संस्करणों ( <= 2.1) के लिए, इसे हल करने के कई तरीके हैं, लेकिन (मेरी राय में) कोई स्पष्ट "सबसे अच्छा तरीका" नहीं है। इस पोस्ट के अन्य उत्तर देखें।


मैं पोस्ट करने वाला था: P वहाँ का उपयोग कर के बीच कोई प्रत्यक्ष अंतर है countके बजाय size/ length?
बर्फ ツ

1
@ सागरपंड्या नहीं, कोई अंतर नहीं है। इसके विपरीत Array#sizeऔर Array#length, एक वैकल्पिक तर्क या ब्लॉक ले Array#count सकते हैं; लेकिन अगर न तो इसका उपयोग किया जाता है तो इसका कार्यान्वयन समान है। अधिक विशेष रूप से, सभी तीन विधियाँ LONG2NUM(RARRAY_LEN(ary))हुड के नीचे कॉल करती हैं: गिनती / लंबाई
टॉम लॉर्ड

1
यह मुहावरेदार रूबी का इतना अच्छा उदाहरण है। बहुत बढ़िया जवाब।
slhck

1
अतिरिक्त श्रेय! गिनती के आधार पर छाँटें.group_by(&:itself).transform_values(&:count).sort_by{|k, v| v}.reverse
Abram

2
@ आप कर सकते हैं sort_by{ |k, v| -v}, कोई reverseजरूरत नहीं ! ;-)
सोनी सेंटोस

26

अब रूबी 2.2.0 का उपयोग करके आप itselfविधि का लाभ उठा सकते हैं ।

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
counts = {}
names.group_by(&:itself).each { |k,v| counts[k] = v.length }
# counts > {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

3
सहमत हों, लेकिन मैं नामों को थोड़ा पसंद करता हूं। समूह_को (और: स्वयं) .मैप {| k, v | [k, v.count]} ._ to_h ताकि आपको कभी भी हैश ऑब्जेक्ट घोषित न करना पड़े
एंडी डे

8
@andrewkday इसे एक कदम आगे ले जाते हुए, रूबी v2.4 ने यह तरीका जोड़ा: Hash#transform_valuesजो हमें आपके कोड को और भी सरल बनाने की अनुमति देता है:names.group_by(&:itself).transform_values(&:count)
टॉम लॉर्ड

इसके अलावा, यह एक बहुत ही सूक्ष्म बिंदु है (जो संभवतः भविष्य के पाठकों के लिए प्रासंगिक नहीं है!), लेकिन ध्यान दें कि आपका कोड भी उपयोग करता है Array#to_h- जिसे रूबी v2.1.0 में जोड़ा गया था (दिसंबर 2013 को जारी किया गया था - अर्थात मूल प्रश्न के लगभग 3 साल बाद) पूछा गया था!)
टॉम लॉर्ड

17

वास्तव में एक डेटा संरचना है जो ऐसा करती है MultiSet:।

दुर्भाग्य से, MultiSetरूबी कोर लाइब्रेरी या मानक पुस्तकालय में कोई कार्यान्वयन नहीं है , लेकिन वेब पर तैरते हुए कुछ कार्यान्वयन हैं।

यह एक महान उदाहरण है कि डेटा संरचना की पसंद एक एल्गोरिथ्म को कैसे सरल बना सकती है। वास्तव में, इस विशेष उदाहरण में, एल्गोरिथ्म भी पूरी तरह से चला जाता है। यह सचमुच है:

Multiset.new(*names)

और बस। उदाहरण, https://GitHub.Com/Josh/Multimap/ का उपयोग कर :

require 'multiset'

names = %w[Jason Jason Teresa Judah Michelle Judah Judah Allison]

histogram = Multiset.new(*names)
# => #<Multiset: {"Jason", "Jason", "Teresa", "Judah", "Judah", "Judah", "Michelle", "Allison"}>

histogram.multiplicity('Judah')
# => 3

उदाहरण, http://maraigue.hhiro.net/multiset/index-en.php का उपयोग करते हुए :

require 'multiset'

names = %w[Jason Jason Teresa Judah Michelle Judah Judah Allison]

histogram = Multiset[*names]
# => #<Multiset:#2 'Jason', #1 'Teresa', #3 'Judah', #1 'Michelle', #1 'Allison'>

क्या मल्टीसेट अवधारणा गणित, या किसी अन्य प्रोग्रामिंग भाषा से उत्पन्न होती है?
एंड्रयू ग्रिम

2
@ एंड्री ग्रिम: दोनों शब्द "मल्टीसेट" (डी ब्रूजन, 1970) और अवधारणा (डेडेकिंड 1888) गणित में उत्पन्न हुए थे। Multisetसख्त गणितीय नियमों द्वारा शासित होता है और ठेठ सेट ऑपरेशन (संघ, चौराहे, पूरक, ...) का समर्थन करता है, जो ज्यादातर "सामान्य" गणितीय सेट सिद्धांत के स्वयंसिद्ध, कानूनों और प्रमेय के अनुरूप होता है, हालांकि कुछ महत्वपूर्ण कानून करते हैं जब आप उन्हें मल्टीसेट करने के लिए सामान्यीकृत करने की कोशिश करते हैं, तो पकड़ रखें। लेकिन यह मामला मेरी समझ से परे है। मैं उन्हें एक गणितीय डेटा संरचना के रूप में उपयोग करता हूं, गणितीय अवधारणा के रूप में नहीं।
जोर्ग डब्ल्यू मित्तग

उस बिंदु पर थोड़ा विस्तार करने के लिए : "... एक तरह से जो कि ज्यादातर स्वयंसिद्ध है ..." : "सामान्य" सेट को आमतौर पर स्वयंसिद्धों (मान्यताओं) के एक सेट द्वारा परिभाषित किया जाता है जिसे "जर्मेलो-फ्रैंकल सेट सिद्धांत" कहा जाता है। "। हालाँकि, इनमें से एक स्वयंसिद्ध: अलौकिकता का स्वयंसिद्ध बताता है कि एक सेट को इसके सदस्यों द्वारा सटीक रूप से परिभाषित किया गया है - जैसे {A, A, B} = {A, B}। यह स्पष्ट रूप से बहु-सेट की परिभाषा का उल्लंघन है!
टॉम भगवान

... हालाँकि, बहुत अधिक विस्तार में जाने के बिना (जैसा कि यह एक सॉफ्टवेयर फोरम है, उन्नत गणित नहीं है!), कोई औपचारिक रूप से क्रिस्प सेट, पीनो एक्सिओम्स और अन्य मल्टीसेट-विशिष्ट स्वयंसिद्धों के लिए स्वयंसिद्ध रूप से बहु-सेट को गणितीय रूप से परिभाषित कर सकता है
टॉम भगवान

13

Enumberable#each_with_object अंतिम हैश लौटने से बचाता है।

names.each_with_object(Hash.new(0)) { |name, hash| hash[name] += 1 }

यह दिखाता है:

=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

सहमत each_with_objectहूं , वैरिएंट मेरे लिए अधिक पठनीय हैinject
लेव लुकोम्स्की

9

रूबी 2.7+

रूबी 2.7 Enumerable#tallyइस सटीक उद्देश्य के लिए शुरू कर रहा है। यहाँ एक अच्छा सारांश है

इस उपयोग के मामले में:

array.tally
# => { "Jason" => 2, "Judah" => 3, "Allison" => 1, "Teresa" => 1, "Michelle" => 1 }

जारी की जा रही सुविधाओं पर डॉक्स यहाँ हैं

आशा है कि यह किसी की मदद करता है!


शानदार खबर!
tadman

6

यह काम।

arr = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
result = {}
arr.uniq.each{|element| result[element] = arr.count(element)}

2
+1 एक अलग दृष्टिकोण के लिए - हालांकि इसमें बदतर सैद्धांतिक जटिलता है - O(n^2)(जो कुछ मूल्यों के लिए मायने रखेगा n) और अतिरिक्त काम करता है (उदाहरण के लिए, "जुदा" 3x के लिए गणना करना है) !. मैं eachइसके बजाय यह भी सुझाव map

उसके लिए धन्यवाद! मैंने मानचित्र को प्रत्येक में बदल दिया है। इसके अलावा, मैंने इसके माध्यम से जाने से पहले सरणी को uniq'ed किया है। शायद अब जटिलता मुद्दा हल हो गया है?
श्रेयस

6

निम्नलिखित एक अधिक कार्यात्मक प्रोग्रामिंग शैली है:

array_with_lower_case_a = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
hash_grouped_by_name = array_with_lower_case_a.group_by {|name| name}
hash_grouped_by_name.map{|name, names| [name, names.length]}
=> [["Jason", 2], ["Teresa", 1], ["Judah", 3], ["Michelle", 1], ["Allison", 1]]

इसका एक फायदा group_byयह है कि आप इसे समूह समतुल्य उपयोग कर सकते हैं, लेकिन बिल्कुल समान आइटम नहीं:

another_array_with_lower_case_a = ["Jason", "jason", "Teresa", "Judah", "Michelle", "Judah Ben-Hur", "JUDAH", "Allison"]
hash_grouped_by_first_name = another_array_with_lower_case_a.group_by {|name| name.split(" ").first.capitalize}
hash_grouped_by_first_name.map{|first_name, names| [first_name, names.length]}
=> [["Jason", 2], ["Teresa", 1], ["Judah", 3], ["Michelle", 1], ["Allison", 1]]

क्या मैंने कार्यात्मक प्रोग्रामिंग सुनी? +1 :-) यह निश्चित रूप से सबसे अच्छा तरीका है, हालांकि यह तर्क दिया जा सकता है कि मेमोरी-कुशल नहीं है। सूचना यह भी है कि पहलुओं में एक # आवृत्ति होती है।
टोकन ऑक्ट


3
names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
Hash[names.group_by{|i| i }.map{|k,v| [k,v.size]}]
# => {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

2

यहाँ महान कार्यान्वयन के बहुत सारे।

लेकिन एक शुरुआत के रूप में मैं इसे पढ़ने और लागू करने के लिए सबसे आसान समझूंगा

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

name_frequency_hash = {}

names.each do |name|
  count = names.count(name)
  name_frequency_hash[name] = count  
end
#=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}

हमने जो कदम उठाए:

  • हमने हैश बनाया
  • हम पर पाला names सरणी
  • हमने गिना कि namesसरणी में प्रत्येक नाम कितनी बार दिखाई दिया
  • हमने एक nameऔर मान का उपयोग करके एक कुंजी बनाईcount

यह थोड़ा और अधिक हो सकता है (और प्रदर्शन के हिसाब से आप ओवरराइडिंग कीज़ के साथ कुछ अनावश्यक काम कर रहे होंगे), लेकिन मेरी राय में आप जो हासिल करना चाहते हैं उसे पढ़ना और समझना आसान है


2
मैं यह नहीं देखता कि स्वीकार किए गए उत्तर की तुलना में पढ़ना कितना आसान है, और यह स्पष्ट रूप से एक बदतर डिजाइन है (बहुत सारे अनावश्यक काम कर रहा है)।
टॉम लॉर्ड

@ भगवान - मैं प्रदर्शन पर आपसे सहमत हूं (मैंने अपने उत्तर में यह भी उल्लेख किया है) - लेकिन एक शुरुआत के रूप में वास्तविक कोड और आवश्यक कदमों को समझने की कोशिश कर रहा हूं, मुझे लगता है कि यह अधिक क्रिया होने में मदद करता है और फिर कोई भी सुधार करने के लिए प्रतिक्षेपक हो सकता है प्रदर्शन और कोड को अधिक घोषित करें
सामी बीरनबम

1
मैं @SamiBirnbaum से कुछ हद तक सहमत हूँ। यह एकमात्र ऐसा है जो लगभग कोई विशेष रूबी ज्ञान का उपयोग नहीं करता है Hash.new(0)। स्यूडोकोड के सबसे करीब। यह पठनीयता के लिए एक अच्छी बात हो सकती है, लेकिन अनावश्यक काम करना भी पाठकों के लिए पठनीयता को नुकसान पहुंचा सकता है जो इसे नोटिस करते हैं क्योंकि अधिक जटिल मामलों में वे यह सोचने में थोड़ा समय बिताएंगे कि वे पागल हो रहे हैं यह जानने की कोशिश कर रहे हैं कि ऐसा क्यों किया गया है।
अदामंतिश

1

यह एक उत्तर की तुलना में अधिक टिप्पणी है, लेकिन एक टिप्पणी इसे न्याय नहीं करेगी। यदि आप करते हैं Array = foo, तो आप आईआरबी के कम से कम एक कार्यान्वयन को क्रैश करते हैं :

C:\Documents and Settings\a.grimm>irb
irb(main):001:0> Array = nil
(irb):1: warning: already initialized constant Array
=> nil
C:/Ruby19/lib/ruby/site_ruby/1.9.1/rbreadline.rb:3177:in `rl_redisplay': undefined method `new' for nil:NilClass (NoMethodError)
        from C:/Ruby19/lib/ruby/site_ruby/1.9.1/rbreadline.rb:3873:in `readline_internal_setup'
        from C:/Ruby19/lib/ruby/site_ruby/1.9.1/rbreadline.rb:4704:in `readline_internal'
        from C:/Ruby19/lib/ruby/site_ruby/1.9.1/rbreadline.rb:4727:in `readline'
        from C:/Ruby19/lib/ruby/site_ruby/1.9.1/readline.rb:40:in `readline'
        from C:/Ruby19/lib/ruby/1.9.1/irb/input-method.rb:115:in `gets'
        from C:/Ruby19/lib/ruby/1.9.1/irb.rb:139:in `block (2 levels) in eval_input'
        from C:/Ruby19/lib/ruby/1.9.1/irb.rb:271:in `signal_status'
        from C:/Ruby19/lib/ruby/1.9.1/irb.rb:138:in `block in eval_input'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:189:in `call'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:189:in `buf_input'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:103:in `getc'
        from C:/Ruby19/lib/ruby/1.9.1/irb/slex.rb:205:in `match_io'
        from C:/Ruby19/lib/ruby/1.9.1/irb/slex.rb:75:in `match'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:287:in `token'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:263:in `lex'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:234:in `block (2 levels) in each_top_level_statement'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:230:in `loop'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:230:in `block in each_top_level_statement'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:229:in `catch'
        from C:/Ruby19/lib/ruby/1.9.1/irb/ruby-lex.rb:229:in `each_top_level_statement'
        from C:/Ruby19/lib/ruby/1.9.1/irb.rb:153:in `eval_input'
        from C:/Ruby19/lib/ruby/1.9.1/irb.rb:70:in `block in start'
        from C:/Ruby19/lib/ruby/1.9.1/irb.rb:69:in `catch'
        from C:/Ruby19/lib/ruby/1.9.1/irb.rb:69:in `start'
        from C:/Ruby19/bin/irb:12:in `<main>'

C:\Documents and Settings\a.grimm>

ऐसा इसलिए Arrayहै क्योंकि एक वर्ग है।


1
arr = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

arr.uniq.inject({}) {|a, e| a.merge({e => arr.count(e)})}

समय 0.028 मिलीसेकंड हो गया

दिलचस्प बात यह है कि, स्टुपिजेक के कार्यान्वयन को बेंचमार्क किया गया:

समय ०.०४१ मिलीसेकंड हो गया

और जीतने वाला जवाब:

समय 0.011 मिलीसेकंड हो गया

:)

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