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

Kyl*_*rtz 2 ruby sorting hash key-value alphabetical

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

给出以下哈希:

pets_ages = {"Eric" => 6, "Harry" => 3, "Georgie" => 12, "Bogart" => 4, "Poly" => 4,
        "Annie" => 1, "Dot" => 3}
Run Code Online (Sandbox Code Playgroud)

并运行以下方法:

pets_ages.sort {|x, y| x[1] <=> y[1]}.to_h
Run Code Online (Sandbox Code Playgroud)

返回以下内容:

{
      "Annie" => 1,
        "Dot" => 3,
      "Harry" => 3,
       "Poly" => 4,
     "Bogart" => 4,
       "Eric" => 6,
    "Georgie" => 12
}
Run Code Online (Sandbox Code Playgroud)

您会注意到散列很好地按照预期的值排序.我想改变的是键的排序,以便在打平时保持字母顺序.注意"Dot"和"Harry"在这方面是正确的,但由于某种原因,"Poly"和"Bogart"不是.我的理论是,它在绑定的情况下按长度自动排序键,而不是按字母顺序排序.我怎么能改变呢?

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

散列#sort将返回一[k, v]对数组,但这些k, v对可以按照您想要的单个过程中的任何条件进行排序.一旦我们有了排序对,我们就把它变回Hash with 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
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,只要你总是在集合中返回一些东西(-1 0 1),你的排序函数可以做你想做的任何事情,所以你可以根据自己的喜好编写它们.但是,由于超级方便的<=>运算符,在Ruby中几乎不需要这种冗长的形式!

正如Stefan指出的那样,你有一个很大的捷径:Array#<=>足以比较比较数组之间的每个条目.这意味着我们可以做以下事情:

pets_ages.sort {|a, b| a.reverse <=> b.reverse }.to_h
Run Code Online (Sandbox Code Playgroud)

这需要每个[k,v]对,将其反转为[v,k],并使用Array#<=>进行比较.由于您需要在每个[k,v]对上执行相同的操作,因此您可以使用#sort_by进一步缩短它

pets_ages.sort_by {|k, v| [v, k] }.to_h
Run Code Online (Sandbox Code Playgroud)

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