如何计算Ruby数组中相同的字符串元素

时间:2011-02-26 16:38:21

标签: ruby arrays count element

我有以下Array = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]

如何为每个相同的元素生成计数

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

生成哈希其中:

其中:     hash = {“Jason”=> 2,“犹大”=> 3,“Allison”=> 1,“Teresa”=> 1,“Michelle”=> 1}

15 个答案:

答案 0 :(得分:110)

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

给你

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

答案 1 :(得分:67)

names = ["Jason", "Jason", "Teresa", "Judah", "Michelle", "Judah", "Judah", "Allison"]
counts = Hash.new(0)
names.each { |name| counts[name] += 1 }
# => {"Jason" => 2, "Teresa" => 1, ....

答案 2 :(得分:27)

Ruby v2.4 +(当前)

首次询问此问题时(2011年2月),标准ruby中无法使用以下代码,因为它使用:

Ruby的这些现代补充实现了以下实现:

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 +(已弃用)

如果使用较旧的ruby版本,无法访问上述Hash#transform_values方法,则可以改为使用Array#to_h,它已添加到Ruby v2.1.0(2013年12月发布):

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

对于更老的ruby版本(<= 2.1),有几种方法可以解决这个问题,但(在我看来)并没有明确的“最佳”方式。请参阅这篇文章的其他答案。

(2019年2月)编辑:

Ruby v2.7 +(尚未发布)

将此评论视为未来的占位符;我将在发布ruby 2.7.0(预计于2019年12月)时更新此帖子,以确认该方法何时使用核心语言。

最近有enhancement to the language。如果一切按计划进行,我们应该会看到一个新方法Enumerable#tally,添加到ruby v2.7.0。此方法专门为此问题添加了新语法:

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

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

答案 3 :(得分:25)

现在使用Ruby 2.2.0,您可以利用itself method

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}

答案 4 :(得分:16)

实际上有一个数据结构可以做到这一点:MultiSet

不幸的是,Ruby核心库或标准库中没有MultiSet实现,但是有一些实现在Web上。

这是数据结构选择如何简化算法的一个很好的例子。事实上,在这个特定的例子中,算法甚至完全消失了。它实际上只是:

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'>

答案 5 :(得分:12)

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}

答案 6 :(得分: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]]

答案 7 :(得分:5)

这很有效。

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

答案 8 :(得分:4)

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}

信用Frank Wambutt

答案 9 :(得分:2)

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}

答案 10 :(得分: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
  • 创建了一个值

它可能稍微冗长一点(性能明智,你会用重写键做一些不必要的工作),但在我看来更容易阅读和理解你想要实现的目标

答案 11 :(得分:1)

这更多是评论而不是答案,但评论不会公正。如果你执行Array = foo,则至少会导致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>

那是因为Array是一个类。

答案 12 :(得分:1)

Ruby 2.7 +

Ruby 2.7为此引入了Enumerable#tallyhere有一个很好的摘要。

在这种情况下:

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

有关已发布功能的文档为here

希望这对某人有帮助!

答案 13 :(得分:0)

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

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

时间过去了0.028毫秒

有趣的是,stupidgeek的实现基准测试了:

时间过去了0.041毫秒

和获胜的答案:

时间过去了0.011毫秒

:)

答案 14 :(得分:0)

使用更简单的 ruby​​ 2.6

names.to_h{ |name| [name, names.count(name)] }

给你

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