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

use*_*520 71 ruby arrays element count

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

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

Where:
"Jason" = 2, "Judah" = 3, "Allison" = 1, "Teresa" = 1, "Michelle" = 1?
Run Code Online (Sandbox Code Playgroud)

产生哈希在哪里:

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

Mau*_*cio 116

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

给你

{"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1} 
Run Code Online (Sandbox Code Playgroud)

  • 如果使用`each_with_object`而不是`inject`,则不必在块中返回(`; total`). (14认同)
  • 对于后代,这是@mfilej的意思:`array.each_with_object(Hash.new(0)){| string,hash | hash [string] + = 1}` (11认同)
  • 从 Ruby 2.7 开始,你可以简单地做:`names.tally`。 (6认同)
  • +1与所选答案一样,但我更喜欢使用注入而不使用"外部"变量. (2认同)

Dyl*_*kow 73

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


Tom*_*ord 40

首次询问此问题时(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}
Run Code Online (Sandbox Code Playgroud)

(2019年2月)编辑:在撰写本文时,此功能尚不可用,因此请将此评论视为未来的占位符...

但最近这种语言得到了改进.如果一切按计划进行,我们应该会看到一种新的方法Hash#transform_values,添加到ruby v2.7.0(将于2019年12月发布).此方法专门为此问题添加了新语法!

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

我将在发布ruby 2.7.0时编辑此注释,以确认该方法何时使用核心语言.

  • @Abram 你可以 `sort_by{ |k, v| -v}`,不需要`reverse`!;-) (2认同)

Ahm*_*hmy 25

现在使用Ruby 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}
Run Code Online (Sandbox Code Playgroud)

  • @andrewkday更进一步,ruby v2.4添加了方法:`Hash#transform_values`,它允许我们更简化代码:`names.group_by(&:本身).transform_values(&:count)` (8认同)
  • 同意,但我稍微偏爱names.group_by(&:itself).map {| k,v | [k,v.count]}。to_h,因此您不必声明哈希对象 (2认同)

Jör*_*tag 16

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

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

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

Multiset.new(*names)
Run Code Online (Sandbox Code Playgroud)

就是这样.例如,使用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
Run Code Online (Sandbox Code Playgroud)

例如,使用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'>
Run Code Online (Sandbox Code Playgroud)

  • @Andrew Grimm:他***"multiset"(de Bruijn,1970s)和*概念*(Dedekind 1888)都起源于数学."Multiset"受严格的数学规则支配,并支持典型的集合运算(并集,交集,补集,...),其方式*大多数*与"正常"数学集理论的公理,定律和定理一致,虽然当你试图将它们推广到多重集合时,一些重要的法则不会*保持不变.但这超出了我对此事的理解.我将它们用作编程数据结构,而不是数学概念. (2认同)

Anc*_*nia 13

Enumberable#each_with_object 避免返回最终的哈希值.

names.each_with_object(Hash.new(0)) { |name, hash| hash[name] += 1 }
Run Code Online (Sandbox Code Playgroud)

返回:

=> {"Jason"=>2, "Teresa"=>1, "Judah"=>3, "Michelle"=>1, "Allison"=>1}
Run Code Online (Sandbox Code Playgroud)


And*_*imm 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]]
Run Code Online (Sandbox Code Playgroud)

一个优点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]]
Run Code Online (Sandbox Code Playgroud)


Shr*_*yas 5

这有效.

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

  • +1对于一种不同的方法 - 虽然这有更糟的理论复杂性 - "O(n ^ 2)"(这对于"n"的某些值很重要)*和*做了额外的工作(它必须算作"犹大" "例如3x!" 我还建议`each`而不是`map`(地图结果被丢弃) (2认同)

Aru*_*hit 5

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}
Run Code Online (Sandbox Code Playgroud)


nar*_*ero 5

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}
Run Code Online (Sandbox Code Playgroud)

信贷弗兰克Wambutt


SRa*_*ack 5

Ruby 2.7以上

Ruby 2.7正是Enumerable#tally为此目的而引入的。有一个很好的总结在这里

在这种情况下:

array.tally
# => { "Jason" => 2, "Judah" => 3, "Allison" => 1, "Teresa" => 1, "Michelle" => 1 }
Run Code Online (Sandbox Code Playgroud)

有关正在发布的功能的文档在这里

希望这对某人有帮助!