如何在按值对哈希进行排序时保留键的字母顺序

时间:2015-04-01 23:11:12

标签: ruby sorting hash key-value alphabetical

初学者在这里。我的第一个问题。放轻松我。

给出以下哈希:

pets_ages = {"Eric" => 6, "Harry" => 3, "Georgie" => 12, "Bogart" => 4, "Poly" => 4,
        "Annie" => 1, "Dot" => 3}

并运行以下方法:

pets_ages.sort {|x, y| x[1] <=> y[1]}.to_h

返回以下内容:

{
      "Annie" => 1,
        "Dot" => 3,
      "Harry" => 3,
       "Poly" => 4,
     "Bogart" => 4,
       "Eric" => 6,
    "Georgie" => 12
}

您会注意到哈希按照预期的值很好地按值排序。我想要改变的是键的排序,因此在平局的情况下它们保持字母顺序。注意&#34; Dot&#34;和#34;哈利&#34;在这方面是正确的,但出于某种原因&#34; Poly&#34;和&#34; Bogart&#34;不是。我的理论是,它在绑定的情况下按长度自动排序键,而不是按字母顺序排序。我怎么能改变它?

3 个答案:

答案 0 :(得分:5)

在许多语言中,Hashes / Dicts没有订购,因为它们是如何在封面下实施的。 Ruby 1.9+非常适合保证订购。

您可以一次性完成此操作 - Ruby允许您按任意标准排序。

# Given
pets_ages = {"Eric" => 6, "Harry" => 3, "Georgie" => 12, "Bogart" => 4, "Poly" => 4, "Annie" => 1, "Dot" => 3}

# Sort pets by the critera of "If names are equal, sort by name, else, sort by age"
pets_ages.sort {|(n1, a1), (n2, a2)| a1 == a2 ? n1 <=> n2 : a1 <=> a2 }.to_h

# => {"Annie"=>1, "Dot"=>3, "Harry"=>3, "Bogart"=>4, "Poly"=>4, "Eric"=>6, "Georgie"=>12}

哈希#sort将返回[k, v]对的数组,但是这些k, v对可以按单个传递中的任何条件进行排序。一旦我们得到了排序对,我们就把它变成一个带有Array#to_h(Ruby 2.1+)的哈希,或者你可以在早期版本中使用Hash[sorted_result],正如Beartech指出的那样。

您可以在排序块中获得所需的复杂内容;如果您熟悉Javascript排序,Ruby实际上在这里工作相同。 <=>方法返回-1,0或1,具体取决于对象之间的比较方式。 #sort只是期望其中一个返回值,它告诉它两个给定值如何相互关联。如果你不想,你根本不需要使用<=> - 这样的东西相当于更紧凑的形式:

pets_ages.sort do |a, b|
  if a[1] == b[1]
    if a[0] > b[0]
      1
    elsif a[0] < b[0]
      -1
    else
      0
    end
  else
    if a[1] > b[1]
      1
    elsif a[1] < b[1]
      -1
    end
  end
end

正如你所看到的,只要你总是在集合中返回一些东西(-1 0 1),你的排序功能可以做你想做的任何事情,所以你可以根据自己的喜好进行组合。然而,在Ruby中几乎不需要这种冗长的形式,因为超级方便的&lt; =&gt;操作者!

正如Stefan指出的那样,你有一个很大的捷径:数组#&lt; =&gt;非常适合compare each entry between the compared arrays。这意味着我们可以做类似的事情:

pets_ages.sort {|a, b| a.reverse <=> b.reverse }.to_h

这需要每个[k,v]对,将其反转为[v,k],并使用Array#&lt; =&gt;比较它。由于您需要在每个[k,v]对上执行相同的操作,您可以使用#sort_by

进一步缩短它
pets_ages.sort_by {|k, v| [v, k] }.to_h

这样做对于每个散列条目,它将键和值传递给块,并且块的返回结果用于将此[k,v]对与其他条目进行比较。由于将[v,k]与另一个[v,k]对比较给出了我们想要的结果,我们只返回一个由[v,k]组成的数组,sort_by通过以下方式收集和排序原始的[k,v]对。

答案 1 :(得分:1)

正如菲利普指出的那样,哈希并不是为了保持秩序,尽管我认为他们可能会使用最新的Ruby。但是,让我们说他们没有。这是一个基于数组的解决方案,然后可以重新散列:

编辑,这是一个单行:

 new_pets_ages = Hash[pets_ages.sort.sort_by {|a| a[1]}]

上一个回答:

pets_ages = {"Eric" => 6, "Harry" => 3, "Georgie" => 12, "Bogart" => 4, "Poly" => 4,
    "Annie" => 1, "Dot" => 3}

arr = pets_ages.sort

# [["Annie", 1], ["Bogart", 4], ["Dot", 3], ["Eric", 6], ["Georgie", 12],
#    ["Harry", 3], ["Poly", 4]]

new_arr = arr.sort_by {|a| a[1]}

#[["Annie", 1], ["Dot", 3], ["Harry", 3], ["Bogart", 4], ["Poly", 4], ["Eric", 6],
#    ["Georgie", 12]]

最后得到哈希:

h = Hash[new_arr]

#{"Annie"=>1, "Dot"=>3, "Harry"=>3, "Bogart"=>4, "Poly"=>4, "Eric"=>6, 
#   "Georgie"=>12}

因此,当我们对哈希进行排序时,它会为我们提供一个数组数组,其中的项目按原始键排序。然后我们按照每个数组的第二个值对数组数组进行排序,因为它是一个惰性排序,它只在需要时才移动它们。然后我们可以将它发送回哈希。我确信有一种方法可以在一行中进行两遍排序,但这看起来很简单。

答案 2 :(得分:0)

正如您已经知道的那样,您使用的红宝石分选方法很少。所以我不会详细解释你,而是为你保留一个非常简单的衬垫。这是你的答案:

pets_ages.sort.sort_by{|pets| pets[1]}.to_h

由于