【问题标题】:Sort an array and store the counters in another array in Ruby对数组进行排序并将计数器存储在 Ruby 中的另一个数组中
【发布时间】:2020-10-02 23:48:17
【问题描述】:

我目前正在使用以下代码在 Ruby 中对一个数组(数字)进行排序:

grapes_sorted = vintages_grapes.group_by(&:itself).sort_by do |k, v| -v.size end.map(&:first) 

效果很好。 但是,我想将每个排序对应的计数器存储在另一个数组中。

我试过了:

grapes_sorted_counters = []
grapes_sorted = vintages_grapes.group_by(&:itself).sort_by do |k, v| 
                        -v.size 
                        grapes_sorted_counters << v.size 
                    end.map(&:first) 

它存储计数器,但是,排序被破坏,没有按应有的顺序排列。我想 v.size(而不是 -v.size)是块中问题的原因。

如何将出现次数正确存储在 grapes_sorted_counters[] 中? 谢谢。

【问题讨论】:

  • 我很好奇你认为-v.size 在做什么。
  • 先按最大值对计数器进行排序。
  • 并非如此。 sort_by 使用块的返回值作为其排序值进行排序,即v.size-v.size 不是该区块看到的最后一件事。实际上,为此使用sort_by 是错误的方法,您应该使用sort,因为它会更快。我会在回答中解释。

标签: arrays ruby sorting


【解决方案1】:

是这部分让你搞砸了:

sort_by do |k, v| 
  -v.size 
  grapes_sorted_counters << v.size 
end

您正在尝试按-v.size 排序。但是块的返回值grapes_sorted_counters &lt;&lt; v.size

所以只需切换这些行的顺序即可。

【讨论】:

  • 我发现你的想法合乎逻辑,但经过测试它不起作用。排序很好,但发现的出现次数是相反的。这与我在帖子中写的相反。 ;)
【解决方案2】:

无论如何,我认为您的排序存在错误。

我目前正在使用以下代码在 Ruby 中对一组(数字)数组进行排序:

我假设输入值是这样的

vintages_grapes = [[3,2], [3,2,1], [3]]

所以在第一步你做一个group_by

vintages_grapes.group_by(&:itself)
# -> {[3, 2]=>[[3, 2]], [3, 2, 1]=>[[3, 2, 1]], [3]=>[[3]]}

您可以看到这些值都嵌套在另一个数组[] 中(它们的大小都是1)。

sort_by do |key, value| 
  -value.size # size 1 for all values
end

group_by 不是必须的,你可以这样做

result = []
vintages_grapes.sort_by do |array| 
  result << array.size
  result.sort!
  -array.size
end

【讨论】:

  • 嗨,克里斯蒂安。此代码似乎只计算每个子数组中的数字。它不计算每个子数组的出现次数。 ;)
  • 这是在做问题中要求的事情(对数组进行排序)。请更新您的问题并提供输入数据,否则它只是在猜测请求的内容。
【解决方案3】:

我相信您的代码存在问题,因此我会建议一种替代方法。

grapes = %w|red blue red green yellow red blue blue green red|
  #> ["red", "blue", "red", "green", "yellow", "red", "blue",
  #   "blue", "green", "red"]

grapes.tally.sort_by { |_,count| -count }.map(&:first)
  #=> ["red", "blue", "green", "yellow"]

Enumerable#tally

步骤如下。

h = grapes.tally
  #=> {"red"=>4, "blue"=>3, "green"=>2, "yellow"=>1} 
a = h.sort_by { |_,count| -count }
  #=> [["red", 4], ["blue", 3], ["green", 2], ["yellow", 1]] 
a.map(&:first)
  #=> ["red", "blue", "green", "yellow"] 

【讨论】:

  • reverse 不会比使用带有负索引的h.sort_by 更快吗?它会强制使用to_a。 "Sort an array in descending order"
  • @theTinMan(又名“Sn”),我最初使用h.sort_by(&amp;:last).reverse 来表示速度(回顾您之前关于性能的评论),但认为-count 阅读更好。
  • 抱歉,我使用了 Ruby 2.6.5,并且需要 2.7。 ;-) 任何解决方法?
  • 亚历克斯,你可以使用grapes.each_with_object(Hash.new(0)) { |grape,h| h[grape] += 1 }.sort_by { |_,count| -count }.map(&amp;:first)。这使用Hash::new 的形式,它接受一个称为默认值 的参数(如果散列h 没有键k,则h[k] 返回的值)。实际上,这是我在@steenstag 提醒我可以使用tally 之前的原始答案。
  • 谢谢卡里。它就像一个魅力。我现在有了良好的排序和同一数组中的出现次数。
【解决方案4】:

我很好奇你认为-v.size 在做什么。 – 铁皮人 39 分钟前
它首先按最大值对计数器进行排序。

sortsort_by 不是这样工作的。两者都依赖于块返回的值来指示如何对值进行排序。

这个例子更像你的代码,你尝试否定size的值,然后返回数组。这将导致升序排序:

foo = [1, 3, 2, 4]
foo.sort_by { |i| 
  -i
   i
 }
 # => [1, 2, 3, 4]

如果将否定值返回到块中,则结果使用降序:

 foo.sort_by { |i| 
  i
  -i
 }
 # => [4, 3, 2, 1]

您的代码正在这样做:

 bar = []
 foo.sort_by { |i|
  -i
  bar << i
}
# => [1, 3, 2, 4]

这导致bar 具有原始订单,因为它的填写顺序与foo 中存在的订单相同,然后您将其传递给map。而且,在排序块内分配总是会这样做。您希望在该块关闭并且 Ruby 返回有序值之后执行此操作。

话虽如此,sort_by 是错误的使用方法,您应该改用sort。为什么在 Ruby 的 sort_by 文档中进行了解释,请查看此处了解更多信息。

此外,最好使用reverse 方法来反转顺序,而不是通过否定值。见“How to sort an array in descending order in Ruby”。

【讨论】:

    猜你喜欢
    • 2022-12-29
    • 1970-01-01
    • 1970-01-01
    • 2012-10-01
    • 2018-08-13
    • 1970-01-01
    • 2019-10-16
    • 2018-06-14
    • 1970-01-01
    相关资源
    最近更新 更多