如何根据 Ruby 中的出现频率对散列进行排序?有没有比我的更好的方法?

How do I sort hash based on frequency of occurrences in Ruby? Is there a better way than what I have?

我正在尝试编写一种方法,该方法接受一个字符串并按降序输出字符在哈希中出现的次数。

我可以为每个单词做这个。我需要为每个角色做这个。

string = "Apple, Banana, Time, Time, Time, Banana Apple, Time."

def cw(string)
  w = string.gsub(/\W/," ").split
  freq = 
  Hash[w.group_by { |x| x }.map { |k, vs| [k, vs.length] }.sort_by { |k, length| -length }]
   return freq
 end
 puts cw(string)

期望:A => 8; B => 2; ..等等

实际:{"Time"=>4,"Apple"=>2,"Banana"=>2}

您需要确保按空字符串拆分:

w = string.gsub(/\W/," ").split("")

我假设每个字母的计数都是大小写无关的。

str = "Apple, Banana, Time, Time, Time, Banana Apple, Time."

您说要按 "the number of occurrences a character" 排序(例如,不是 "letter")。您可以按如下方式进行。

h = str.downcase.
        each_char.
        each_with_object(Hash.new(0)) { |c,h| h[c] += 1 }.
        sort_by { |_,v| -v }.
        to_h
  #=> {"a"=>8, " "=>7, "e"=>6, ","=>6, "i"=>4, "t"=>4, "m"=>4, "p"=>4,
  #    "n"=>4, "b"=>2, "l"=>2, "."=>1}

如果只需要字母数,则需要多一步:

h.select { |k,_| k.match?(/[a-z]/i) } 
  #=> {"a"=>8, "e"=>6, "i"=>4, "t"=>4, "m"=>4, "p"=>4, "n"=>4, "b"=>2, "l"=>2}

这最后一步当然可以链接到上面 h 的计算。

步骤如下

a = str.downcase
  #=> "apple, banana, time, time, time, banana apple, time."
b = a.each_char
  #=> #<Enumerator: "apple, banana, time, time, time, banana apple,
  #                  time.":each_char>

我们可以看到枚举器 c 将生成并通过将其转换为数组传递给块的元素。

b.to_a
  #=> ["a", "p", "p", "l", "e", ",", " ", "b", "a", "n", "a", "n",
  #    "a", ",", " ", "t", "i", "m", "e", ",", " ", "t", "i", "m",
  #    "e", ",", " ", "t", "i", "m", "e", ",", " ", "b", "a", "n",
  #    "a", "n", "a", " ", "a", "p", "p", "l", "e", ",", " ", "t",
  #    "i", "m", "e", "."]

继续,

c = b.each_with_object(Hash.new(0)) { |c,h| h[c] += 1 }
  #=> {"a"=>8, "p"=>4, "l"=>2, "e"=>6, ","=>6, " "=>7, "b"=>2,
  #    "n"=>4, "t"=>4, "i"=>4, "m"=>4, "."=>1}
d = c.sort_by { |_,v| -v }
  #=> [["a", 8], [" ", 7], ["e", 6], [",", 6], ["i", 4], ["t", 4],
  #    ["m", 4], ["p", 4], ["n", 4], ["b", 2], ["l", 2], [".", 1]]
e = d.to_h
  #=> {"a"=>8, " "=>7, "e"=>6, ","=>6, "i"=>4, "t"=>4, "m"=>4,
  #    "p"=>4, "n"=>4, "b"=>2, "l"=>2, "."=>1}
e.select { |k,_| k.match?(/[a-z]/i) }
  #=> {"a"=>8, "e"=>6, "i"=>4, "t"=>4, "m"=>4, "p"=>4, "n"=>4,
  #    "b"=>2, "l"=>2}

如果a的计算改为如下,最后一步可以省略。

a = str.downcase.gsub(/[^a-z]/i, '')
  #=> "applebananatimetimetimebananaappletime

尝试以下面的方式使用 gsub。

string = "Apple, Banana, Time, Time, Time, Banana Apple, Time."

def cw(string)
  a = string.gsub(/[aA]/).count
  b = string.gsub(/[bB]/).count
  Hash['A', a, 'B', b]
end

cw(string)
  => {"A"=>8, "B"=>2}