【问题标题】:Why can't I sort an array of strings by `count`?为什么我不能按 `count` 对字符串数组进行排序?
【发布时间】:2015-12-07 09:10:02
【问题描述】:

使用此代码:

line = ("Ignore punctuation, please :)")

string = line.strip.downcase.split(//)
string.select! {|x| /[a-z]/.match(x) }
string.sort_by!{ |x| string.count(x)}

结果是:

["r", "g", "s", "l", "c", "o", "o", "p", "u", "i", "t", "u", "a", "t", "i", "a", "p", "n", "e", "e", "n", "n", "e"]

在这种情况下按计数排序不起作用吗?为什么?有没有更好的方法来按频率隔离单词?

【问题讨论】:

  • 输出实际上是按升序排序的(它们之间的关系以任何顺序出现)。你到底想做什么?
  • 假设字符串是“abcbabebd”,它应该排序为类似于 [c, d, e, a, a, b, b, b, b] 的东西,其中重复最少的元素是第一个,然后是更多重复的元素是最后一个@Cameron

标签: ruby string sorting


【解决方案1】:

根据您的评论,我想您想按频率和字母顺序对字符进行排序。当唯一的sort_by! 条件为string.count(x) 时,具有相同字符数的频率组可能会相互混合出现。要按字母顺序对每个组进行排序,您必须在 sort_by! 方法中添加第二个条件:

line = ("Ignore punctuation, please :)")

string = line.strip.downcase.split(//)
string.select! {|x| /[a-z]/.match(x) }
string.sort_by!{ |x| [string.count(x), x]}

那么输出会是

["c", "g", "l", "r", "s", "a", "a", "i", "i", "o", "o", "p", "p", "t", "t", "u", "u", "e", "e", "e", "n", "n", "n"]

【讨论】:

    【解决方案2】:

    让我们逐行查看您的代码。

    line = ("Ignore punctuation, please :)")
    s = line.strip.downcase
      #=> "ignore punctuation, please :)"
    

    在这里strip 没有什么特别的原因,因为无论如何你都会删除空格和标点符号。

    string = s.split(//)
      #=> ["i", "g", "n", "o", "r", "e", " ", "p", "u", "n", "c", "t",
      #    "u", "a", "t", "i", "o", "n", ",", " ", "p", "l", "e", "a",
      #    "s", "e", " ", ":", ")"]
    

    您已选择将句子拆分为字符,这很好,但正如我将在最后提到的,您可以只使用 String 方法。无论如何,

    string = s.chars
    

    做同样的事情,可以说更清楚。您现在拥有的是一个名为string 的数组。这不是有点混乱吗?让我们称之为arr

    arr = s.chars
    

    (人们经常看到sstr 用于字符串名称,aarr 用于数组名称,hhash 用于哈希名称,等等。)

    arr.select! {|x| /[a-z]/.match(x) }
      #=> ["i", "g", "n", "o", "r", "e", "p", "u", "n", "c", "t", "u",
      #    "a", "t", "i", "o", "n", "p", "l", "e", "a", "s", "e"] 
    

    现在您已经消除了除小写字母之外的所有字母。你也可以这样写:

    arr.select! {|x| s =~ /[a-z]/ }
    

    arr.select! {|x| s[/[a-z]/] }
    

    您现在可以进行排序了。

    arr.sort_by!{ |x| arr.count(x) }
      #=> ["l", "g", "s", "c", "r", "i", "p", "u", "a", "o", "t", "p",
      #    "a", "t", "i", "o", "u", "n", "n", "e", "e", "n", "e"] 
    

    这没问题,但对数组进行排序并同时计算其元素的频率并不是一个好习惯。更好的是:

    arr1 = arr.sort_by{ |x| arr.count(x) }
    

    给出相同的顺序。生成的排序数组是否正确?让我们计算每个字母在字符串中出现的次数。

    我将创建一个散列,其键是arr 的唯一元素,其值是相关键在arr 中出现的次数。有几种方法可以做到这一点。一个简单但不是很有效的方法如下:

    h = {}
    a = arr.uniq
      #=> ["l", "g", "s", "c", "r", "i", "p", "u", "a", "o", "t", "n", "e"]
    a.each { |c| h[c] = arr.count(c) }
    h #=> {"l"=>1, "g"=>1, "s"=>1, "c"=>1, "r"=>1, "i"=>2, "p"=>2,
      #    "u"=>2, "a"=>2, "o"=>2, "t"=>2, "n"=>3, "e"=>3} 
    

    通常会这样写:

    h = arr.uniq.each_with_object({}) { |c,h| h[c] = arr.count(c) }
    

    h 的元素按价值递增的顺序排列,但这只是巧合。为了确保它们按那个顺序排列(以便更容易查看顺序),我们需要构造一个数组,对其进行排序,然后将其转换为哈希:

    a = arr.uniq.map { |c| [c, arr.count(c)] }
      #=> [["l", 1], ["g", 1], ["s", 1], ["c", 1], ["r", 1], ["a", 2], ["p", 2],
      #    ["u", 2], ["i", 2], ["o", 2], ["t", 2], ["n", 3], ["e", 3]] 
    a = a.sort_by { |_,count| count }
      #=> [["l", 1], ["g", 1], ["s", 1], ["c", 1], ["r", 1], ["a", 2], ["t", 2],
      #    ["u", 2], ["i", 2], ["o", 2], ["p", 2], ["n", 3], ["e", 3]] 
    h = Hash[a]
      #=> {"l"=>1, "g"=>1, "s"=>1, "c"=>1, "r"=>1, "i"=>2, "t"=>2,
      #    "u"=>2, "a"=>2, "o"=>2, "p"=>2, "n"=>3, "e"=>3}
    

    人们通常会看到这样写:

    h = Hash[arr.uniq.map { |c| [c, arr.count(c)] }.sort_by(&:last)]
    

    或者,在 Ruby v2.0+ 中:

    h = arr.uniq.map { |c| [c, arr.count(c)] }.sort_by(&:last).to_h
    

    请注意,在 Ruby 1.9 之前,哈希中没有键排序的概念。

    h 的键值对的值表明您的排序是正确的。然而,它的效率不是很高。那是因为在:

    arr.sort_by { |x| arr.count(x) } 
    

    你反复遍历arr,计算元素的频率。最好在上面构造散列:

    h = arr.uniq.each_with_object({}) { |c,h| h[c] = arr.count(c) }
    

    在执行排序之前,然后:

    arr.sort_by { |x| h[x] }
    

    顺便说一句,让我提一个更有效的方法来构造哈希h,它只需要一次通过arr

    h = Hash.new(0)
    arr.each { |x| h[x] += 1 }
    h #=> {"l"=>1, "g"=>1, "s"=>1, "c"=>1, "r"=>1, "a"=>2, "p"=>2,
      #    "u"=>2, "i"=>2, "o"=>2, "t"=>2, "n"=>3, "e"=>3} 
    

    或者,更简洁:

    h = arr.each_with_object(Hash.new(0)) { |x,h| h[x] += 1 } 
    

    这里h被称为计数哈希

    h = Hash.new(0)
    

    创建一个默认值为零的空散列。这意味着如果h 没有密钥kh[k] 将返回零。 abbreviated assignment h[c] += 1 扩展为:

    h[c] = h[c] + 1
    

    如果h没有键c,则默认值分配给右侧的h[c]

    h[c] = 0 + 1 #=> 1
    

    但是下次遇到c

    h[c] = h[c] + 1
      #=> 1 + 1 => 2
    

    最后,让我们重新开始,尽可能多地使用String 方法:

    line = ("Ignore punctuation, please :)")
    s = line.strip.downcase.gsub(/./) { |c| (c =~ /[a-z]/) ? c : '' }
      #=> "ignorepunctuationplease"
    h = s.each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }
      #=> {"i"=>2, "g"=>1, "n"=>3, "o"=>2, "r"=>1, "e"=>3, "p"=>2,
      #    "u"=>2, "c"=>1, "t"=>2, "a"=>2, "l"=>1, "s"=>1}
    s.each_char.sort_by { |c| h[c] }
      #=> ["l", "g", "s", "c", "r", "i", "p", "u", "a", "o", "t", "p",
      #    "a", "t", "i", "o", "u", "n", "n", "e", "e", "n", "e"]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-03
      • 2019-12-29
      • 2012-10-14
      • 1970-01-01
      • 1970-01-01
      • 2014-07-10
      • 2019-11-24
      • 1970-01-01
      相关资源
      最近更新 更多