J’ai le Array = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
suivant Array = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
Comment puis-je produire un compte pour chaque élément identique ?
Where: "Jason" = 2, "Judah" = 3, "Allison" = 1, "Teresa" = 1, "Michelle" = 1?
ou produire un hash Où:
Où: hash = {“Jason” => 2, “Judah” => 3, “Allison” => 1, “Teresa” => 1, “Michelle” => 1}
names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"] counts = Hash.new(0) names.each { |name| counts[name] += 1 } # => {"Jason" => 2, "Teresa" => 1, ....
names.inject(Hash.new(0)) { |total, e| total[e] += 1 ;total}
vous donne
{"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
Maintenant, en utilisant Ruby 2.2.0, vous pouvez utiliser la méthode itself
– 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}
Il existe en fait une structure de données qui fait ceci: MultiSet
.
Malheureusement, il n’y a pas d’implémentation MultiSet
dans la bibliothèque principale ou la bibliothèque standard de Ruby, mais quelques implémentations flottent sur le Web.
C’est un excellent exemple de la façon dont le choix d’une structure de données peut simplifier un algorithme. En fait, dans cet exemple particulier, l’algorithme disparaît même complètement . C’est littéralement juste:
Multiset.new(*names)
Et c’est tout. Exemple, en utilisant https://GitHub.Com/Josh/Multimap/ :
require 'multiset' names = %w[Jason Jason Teresa Judah Michelle Judah Judah Allison] histogram = Multiset.new(*names) # => # histogram.multiplicity('Judah') # => 3
Exemple, en utilisant http://maraigue.hhiro.net/multiset/index-en.php :
require 'multiset' names = %w[Jason Jason Teresa Judah Michelle Judah Judah Allison] histogram = Multiset[*names] # => #
Le code suivant n’était pas possible dans Ruby standard lorsque cette question a été posée pour la première fois (février 2011), car elle utilise:
Object#itself
– Object#itself
, qui a été ajouté à Ruby v2.2.0 (publié en décembre 2014). Hash#transform_values
, qui a été ajouté à Ruby v2.4.0 (sorti en décembre 2016). Ces ajouts modernes à Ruby permettent la mise en œuvre suivante:
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}
Enumberable#each_with_object
vous évite de retourner le hachage final.
names.each_with_object(Hash.new(0)) { |name, hash| hash[name] += 1 }
Résultats:
=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
Cela marche.
arr = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"] result = {} arr.uniq.each{|element| result[element] = arr.count(element)}
Voici un style de programmation légèrement plus fonctionnel:
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]]
L’un des avantages de group_by
est que vous pouvez l’utiliser pour regrouper des éléments équivalents mais pas exactement identiques:
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]]
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}
Crédit Frank Wambutt
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}
C’est plus un commentaire qu’une réponse, mais un commentaire ne rendrait pas justice. Si vous faites Array = foo
, vous plantez au moins une implémentation d’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 `' C:\Documents and Settings\a.grimm>
C’est parce que Array
est une classe.
Beaucoup de bonnes implémentations ici.
Mais en tant que débutant, je considérerais cela comme le plus facile à lire et à mettre en œuvre.
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}
Les mesures que nous avons sockets:
names
names
name
et une valeur en utilisant le count
Cela peut être légèrement plus verbeux (et sur le plan de la performance, vous allez faire un travail inutile avec des clés prépondérantes), mais à mon avis plus facile à lire et à comprendre pour ce que vous voulez atteindre
arr = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"] arr.uniq.inject({}) {|a, e| a.merge({e => arr.count(e)})}
Temps écoulé 0.028 millisecondes
Fait intéressant, la mise en œuvre de stupidgeek a été évaluée:
Temps écoulé 0.041 millisecondes
et la réponse gagnante:
Temps écoulé 0.011 millisecondes
🙂