Saya memiliki yang berikut ini Array = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
Bagaimana cara menghasilkan hitungan untuk setiap elemen identik ?
Where:
"Jason" = 2, "Judah" = 3, "Allison" = 1, "Teresa" = 1, "Michelle" = 1?
atau menghasilkan hash Dimana:
Di mana: hash = {"Jason" => 2, "Judah" => 3, "Allison" => 1, "Teresa" => 1, "Michelle" => 1}
Enumerable#tally
. Info selengkapnya di sini .Jawaban:
names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"] counts = Hash.new(0) names.each { |name| counts[name] += 1 } # => {"Jason" => 2, "Teresa" => 1, ....
sumber
names.inject(Hash.new(0)) { |total, e| total[e] += 1 ;total}
Memberi anda
{"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
sumber
each_with_object
sebagai penggantiinject
Anda tidak harus mengembalikan (;total
) di blok.array.each_with_object(Hash.new(0)){|string, hash| hash[string] += 1}
names.tally
.Ruby v2.7 + (terbaru)
Mulai ruby v2.7.0 (dirilis Desember 2019), bahasa inti sekarang menyertakan
Enumerable#tally
- metode baru , yang dirancang khusus untuk masalah ini:names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"] names.tally #=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
Ruby v2.4 + (saat ini didukung, tetapi lebih lama)
Kode berikut tidak mungkin dalam ruby standar ketika pertanyaan ini pertama kali diajukan (Februari 2011), karena menggunakan:
Object#itself
, yang ditambahkan ke Ruby v2.2.0 (dirilis Desember 2014).Hash#transform_values
, yang ditambahkan ke Ruby v2.4.0 (dirilis Desember 2016).Penambahan modern pada Ruby memungkinkan implementasi berikut:
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}
Ruby v2.2 + (tidak digunakan lagi)
Jika menggunakan versi ruby yang lebih lama, tanpa akses ke
Hash#transform_values
metode yang disebutkan di atas , Anda dapat menggunakanArray#to_h
, yang telah ditambahkan ke Ruby v2.1.0 (dirilis Desember 2013):names.group_by(&:itself).map { |k,v| [k, v.length] }.to_h #=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
Untuk versi ruby yang lebih lama (
<= 2.1
), ada beberapa cara untuk mengatasinya, tetapi (menurut saya) tidak ada cara "terbaik" yang jelas. Lihat jawaban lain untuk posting ini.sumber
count
selainsize
/length
?Array#size
danArray#length
,Array#count
dapat mengambil argumen atau blok opsional; tetapi jika digunakan dengan keduanya maka implementasinya identik. Lebih khusus lagi, ketiga metode memanggil diLONG2NUM(RARRAY_LEN(ary))
bawah tenda: hitung / panjang.group_by(&:itself).transform_values(&:count).sort_by{|k, v| v}.reverse
sort_by{ |k, v| -v}
, tidakreverse
perlu! ;-)Sekarang menggunakan Ruby 2.2.0 Anda dapat memanfaatkan
itself
metode ini .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}
sumber
Hash#transform_values
yang memungkinkan kami untuk lebih menyederhanakan kode Anda:names.group_by(&:itself).transform_values(&:count)
Array#to_h
- yang telah ditambahkan ke Ruby v2.1.0 (dirilis Desember 2013 - yaitu hampir 3 tahun setelah pertanyaan asli diminta!)Sebenarnya ada struktur data yang melakukan ini:
MultiSet
.Sayangnya tidak ada
MultiSet
implementasi di pustaka inti Ruby atau pustaka standar, tetapi ada beberapa penerapan yang beredar di web.Ini adalah contoh yang bagus tentang bagaimana pilihan struktur data dapat menyederhanakan algoritma. Faktanya, dalam contoh khusus ini, algoritme bahkan sepenuhnya hilang. Ini benar-benar hanya:
Dan itu dia. Contoh, menggunakan 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
Contoh, menggunakan 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'>
sumber
Multiset
diatur oleh aturan matematika yang ketat dan mendukung operasi himpunan tipikal (gabungan, perpotongan, komplemen, ...) dengan cara yang sebagian besar konsisten dengan aksioma, hukum, dan teorema dari teori himpunan matematika "normal", meskipun beberapa hukum penting melakukan tidak tahan saat Anda mencoba menggeneralisasikannya menjadi multiset. Tapi itu jauh di luar pemahaman saya tentang masalah ini. Saya menggunakannya sebagai struktur data pemrograman, bukan sebagai konsep matematika.{A, A, B} = {A, B}
. Ini jelas merupakan pelanggaran terhadap definisi multi-set!Enumberable#each_with_object
menyelamatkan Anda dari mengembalikan hash terakhir.names.each_with_object(Hash.new(0)) { |name, hash| hash[name] += 1 }
Pengembalian:
=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
sumber
each_with_object
varian lebih mudah dibaca oleh saya daripadainject
Ruby 2.7+
Ruby 2.7 diperkenalkan
Enumerable#tally
untuk tujuan yang tepat ini. Ada ringkasan bagus di sini .Dalam kasus penggunaan ini:
array.tally # => { "Jason" => 2, "Judah" => 3, "Allison" => 1, "Teresa" => 1, "Michelle" => 1 }
Dokumen tentang fitur yang dirilis ada di sini .
Semoga ini bisa membantu seseorang!
sumber
Ini bekerja.
arr = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"] result = {} arr.uniq.each{|element| result[element] = arr.count(element)}
sumber
O(n^2)
(yang akan menjadi masalah untuk beberapa nilain
) dan melakukan pekerjaan tambahan (harus dihitung untuk "Judah" 3x, misalnya) !. Saya juga menyarankaneach
alih-alihmap
(hasil peta akan dibuang)Berikut ini adalah gaya pemrograman yang sedikit lebih fungsional:
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]]
Salah satu keuntungannya
group_by
adalah Anda dapat menggunakannya untuk mengelompokkan item yang setara tetapi tidak persis sama: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]]
sumber
a = [1, 2, 3, 2, 5, 6, 7, 5, 5] a.each_with_object(Hash.new(0)) { |o, h| h[o] += 1 } # => {1=>1, 2=>2, 3=>1, 5=>3, 6=>1, 7=>1}
Kredit Frank Wambutt
sumber
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}
sumber
Banyak implementasi bagus di sini.
Tetapi sebagai pemula, saya akan menganggap ini paling mudah untuk dibaca dan diterapkan
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}
Langkah-langkah yang kami ambil:
names
arraynames
larikname
dan nilai menggunakancount
Ini mungkin sedikit lebih bertele-tele (dan dari segi kinerja Anda akan melakukan beberapa pekerjaan yang tidak perlu dengan kunci utama), tetapi menurut saya lebih mudah untuk membaca dan memahami untuk apa yang ingin Anda capai
sumber
Hash.new(0)
. Yang paling dekat dengan pseudocode. Itu bisa menjadi hal yang baik untuk keterbacaan tetapi juga melakukan pekerjaan yang tidak perlu dapat merusak keterbacaan bagi pembaca yang menyadarinya karena dalam kasus yang lebih kompleks mereka akan menghabiskan sedikit waktu untuk berpikir mereka gila mencoba mencari tahu mengapa itu dilakukan.Ini lebih merupakan komentar daripada jawaban, tetapi komentar tidak akan adil. Jika Anda melakukannya
Array = foo
, Anda menghentikan setidaknya satu implementasi IRB: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>
Itu karena
Array
kelas.sumber
arr = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"] arr.uniq.inject({}) {|a, e| a.merge({e => arr.count(e)})}
Waktu berlalu 0,028 milidetik
Menariknya, implementasi bodohgeek mengacu pada:
Waktu berlalu 0,041 milidetik
dan jawaban kemenangan:
Waktu berlalu 0,011 milidetik
:)
sumber