按值对哈希进行排序时如何保留键的字母顺序
How to preserve alphabetical order of keys when sorting a hash by the value
这里是初学者。我的第一个问题。对我放宽点。
给定以下哈希值:
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
}
您会注意到哈希按预期很好地按值排序。我想更改的是键的顺序,以便在平局的情况下它们保持字母顺序。注意 "Dot" 和 "Harry" 在这方面是正确的,但由于某些原因 "Poly" 和 "Bogart" 不是。我的理论是,在平局的情况下,它会自动按长度对键进行排序,而不是按字母顺序排序。我该如何更改?
正如 Philip 指出的那样,哈希并不是为了保持顺序,尽管我认为在最近的 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}
因此,当我们对哈希进行排序时,它会为我们提供一个数组数组,其中的项目按原始键排序。然后我们根据每个数组的第二个值对数组数组进行排序,因为它是惰性排序,所以它只会在需要时移动它们。然后我们可以将它发送回哈希。我敢肯定有一种技巧可以在一行中进行两次排序,但这看起来很简单。
在许多语言中,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}
Hash#sort 将 return 一个 [k, v]
对的数组,但是这些 k, v
对可以在单次传递中按照您想要的任何标准进行排序。一旦我们有了排序的对,我们就把它变成一个哈希 Array#to_h
(Ruby 2.1+),或者你可以在早期版本中使用 Hash[sorted_result]
,正如 Beartech 指出的那样。
你可以在排序块中得到你想要的那么复杂;如果您熟悉 Javascript 排序,Ruby 在这里实际上是一样的。 <=>
方法 returns -1、0 或 1,具体取决于对象之间的比较方式。 #sort 只需要其中一个 return 值,它告诉它两个给定值如何相互关联。如果你不想,你甚至根本不必使用 <=>
- 这样的东西相当于更紧凑的形式:
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
正如你所看到的,只要你总是return集合中的某些东西(-1 0 1),你的排序函数就可以做任何你想做的事情,所以你可以随心所欲地组合它们.然而,在 Ruby 中几乎不需要这样冗长的形式,因为超级方便的 <=> 运算符!
不过,正如 Stefan 指出的那样,这里有一个很大的捷径:Array#<=> 足以 compare each entry between the compared arrays。这意味着我们可以做类似的事情:
pets_ages.sort {|a, b| a.reverse <=> b.reverse }.to_h
这需要每个 [k, v] 对,将其反转为 [v, k],并使用 Array#<=> 进行比较。由于您需要对每个比较的 [k, v] 对执行相同的操作,您可以使用 #sort_by
进一步缩短它
pets_ages.sort_by {|k, v| [v, k] }.to_h
它所做的是针对每个哈希条目,它将键和值传递给块,块的 return 结果用于将此 [k, v] 对与其他对进行比较条目。因为比较 [v, k] 和另一个 [v, k] 对会得到我们想要的结果,我们只是 return 一个由 [v, k] 组成的数组,sort_by 收集和排序原始 [k, v] 对 by.
如您所知,ruby 中您使用过的几种排序方法。所以我不会详细解释它,而是为您保留一个非常简单的衬里。这是你的答案:
pets_ages.sort.sort_by{|pets| pets[1]}.to_h
谢谢
这里是初学者。我的第一个问题。对我放宽点。
给定以下哈希值:
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
}
您会注意到哈希按预期很好地按值排序。我想更改的是键的顺序,以便在平局的情况下它们保持字母顺序。注意 "Dot" 和 "Harry" 在这方面是正确的,但由于某些原因 "Poly" 和 "Bogart" 不是。我的理论是,在平局的情况下,它会自动按长度对键进行排序,而不是按字母顺序排序。我该如何更改?
正如 Philip 指出的那样,哈希并不是为了保持顺序,尽管我认为在最近的 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}
因此,当我们对哈希进行排序时,它会为我们提供一个数组数组,其中的项目按原始键排序。然后我们根据每个数组的第二个值对数组数组进行排序,因为它是惰性排序,所以它只会在需要时移动它们。然后我们可以将它发送回哈希。我敢肯定有一种技巧可以在一行中进行两次排序,但这看起来很简单。
在许多语言中,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}
Hash#sort 将 return 一个 [k, v]
对的数组,但是这些 k, v
对可以在单次传递中按照您想要的任何标准进行排序。一旦我们有了排序的对,我们就把它变成一个哈希 Array#to_h
(Ruby 2.1+),或者你可以在早期版本中使用 Hash[sorted_result]
,正如 Beartech 指出的那样。
你可以在排序块中得到你想要的那么复杂;如果您熟悉 Javascript 排序,Ruby 在这里实际上是一样的。 <=>
方法 returns -1、0 或 1,具体取决于对象之间的比较方式。 #sort 只需要其中一个 return 值,它告诉它两个给定值如何相互关联。如果你不想,你甚至根本不必使用 <=>
- 这样的东西相当于更紧凑的形式:
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
正如你所看到的,只要你总是return集合中的某些东西(-1 0 1),你的排序函数就可以做任何你想做的事情,所以你可以随心所欲地组合它们.然而,在 Ruby 中几乎不需要这样冗长的形式,因为超级方便的 <=> 运算符!
不过,正如 Stefan 指出的那样,这里有一个很大的捷径:Array#<=> 足以 compare each entry between the compared arrays。这意味着我们可以做类似的事情:
pets_ages.sort {|a, b| a.reverse <=> b.reverse }.to_h
这需要每个 [k, v] 对,将其反转为 [v, k],并使用 Array#<=> 进行比较。由于您需要对每个比较的 [k, v] 对执行相同的操作,您可以使用 #sort_by
进一步缩短它pets_ages.sort_by {|k, v| [v, k] }.to_h
它所做的是针对每个哈希条目,它将键和值传递给块,块的 return 结果用于将此 [k, v] 对与其他对进行比较条目。因为比较 [v, k] 和另一个 [v, k] 对会得到我们想要的结果,我们只是 return 一个由 [v, k] 组成的数组,sort_by 收集和排序原始 [k, v] 对 by.
如您所知,ruby 中您使用过的几种排序方法。所以我不会详细解释它,而是为您保留一个非常简单的衬里。这是你的答案:
pets_ages.sort.sort_by{|pets| pets[1]}.to_h
谢谢