当按值对哈希进行排序时,如何保持键的字母顺序

时间:2021-05-05 09:51:03

beginner here. My first question. Go easy on me.

初学者在这里。我的第一个问题。对我。

Given the following hash:

鉴于以下散列:

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

and running the following method:

并按以下方式运行:

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

the following is returned:

以下是返回:

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

You will notice the hash is nicely sorted by the value, as intended. What I'd like to change is the ordering of the keys, so that they remain alphabetical in the case of a tie. Notice "Dot" and "Harry" are correct in that regard, but for some reason "Poly" and "Bogart" are not. My theory is that it is automatically sorting the keys by length in the case of a tie, and not alphabetically. How can I change that?

您将注意到散列按照值进行了很好的排序。我想要改变的是钥匙的顺序,以便在系领带的情况下它们仍然按字母顺序排列。注意“Dot”和“Harry”在这方面是正确的,但是由于某些原因“Poly”和“Bogart”不是。我的理论是,如果是领带,它会自动按长度排序,而不是按字母顺序排序。我怎么才能改变呢?

3 个解决方案

#1


5  

In many languages, Hashes/Dicts aren't ordered, because of how the are implemented under the covers. Ruby 1.9+ is nice enough to guarantee ordering.

在许多语言中,散列/命令不是有序的,因为它们是在幕后实现的。Ruby 1.9+足够好,可以保证订购。

You can do this in a single pass - Ruby allows you to sort by arbitrary criteria.

您可以在一次遍历中完成此操作——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 will return an array of [k, v] pairs, but those k, v pairs can be sorted by any criteria you want in a single pass. Once we have the sorted pairs, we turn it back into a Hash with Array#to_h (Ruby 2.1+), or you can use Hash[sorted_result] in earlier versions, as Beartech points out.

散列#sort将返回一个[k, v]对的数组,但是这些k, v对可以根据您想要的任何条件在单遍中排序。一旦我们有了排序对,我们就把它转换成一个带有#to_h (Ruby 2.1+)数组的散列,或者您可以在早期版本中使用散列[sorted_result], Beartech指出。

You could get as complex as you want in the sort block; if you're familiar with Javascript sorting, Ruby actually works the same here. The <=> method returns -1, 0, or 1 depending on how the objects compare to each other. #sort just expects one of those return values, which tells it how the two given values relate to each other. You don't even have to use <=> at all if you don't want to - something like this is equivalent to the more compact form:

你可以在排序块中得到你想要的最复杂的东西;如果您熟悉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

As you can see, as long as you always return something in the set (-1 0 1), your sort function can do whatever you want, so you can compose them however you'd like. However, such verbose forms are practically never necessary in Ruby, because of the super handy <=> operator!

正如您所看到的,只要您始终返回集合中的某些内容(-1 0 1),您的排序函数可以做任何您想做的事情,因此您可以按照自己的意愿组合它们。然而,这种冗长的表单在Ruby中实际上是没有必要的,因为超级方便的<=>操作符!

As Stefan points out, though, you have a BIG shortcut here: Array#<=> is nice enough to compare each entry between the compared arrays. This means that we can do something like:

正如Stefan指出的,这里有一个很大的快捷方式:数组#<=>非常适合比较数组之间的每个条目。这意味着我们可以做如下事情:

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

This takes each [k, v] pair, reverses it into [v, k], and uses Array#<=> to compare it. Since you need to perform this same operation on each [k, v] pair compared, you can shortcut it even further with #sort_by

这将获取每个[k, v]对,并将其转换为[v, k],并使用数组#<=>对其进行比较。由于您需要对每个[k, v]对执行相同的操作,所以您可以使用#sort_by进一步简化它

pets_ages.sort_by {|k, v| [v, k] }.to_h

What this does is for each hash entry, it passes the key and value to the block, and the return result of the block is what is used to compare this [k, v] pair to other entries. Since comparing [v, k] to another [v, k] pair will give us the result we want, we just return an array consisting of [v, k], which sort_by collects and sorts the original [k, v] pairs by.

对于每个散列条目,它将键和值传递给块,块的返回结果就是用来将这个[k, v]对与其他条目进行比较的结果。由于将[v, k]与另一个[v, k]对进行比较会得到我们想要的结果,所以我们只返回一个由[v, k]组成的数组,sort_by收集并对原始的[k, v]对进行排序。

#2


1  

As Philip pointed out, hashes were not meant to preserve order, though I think in the latest Ruby they might. But let's say they don't. Here's an array based solution that could then be re-hashed:

正如Philip所指出的,散列并不是为了保持秩序,尽管我认为在最新的Ruby中它们可能是这样的。但假设他们没有。这里有一个基于数组的解决方案,然后可以重新散列:

Edit here it is in a one-liner:

编辑在这里,它是一个一行:

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

previous answer:

之前的回答:

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]]

And finally to get a hash back:

最后,为了得到一个散列:

h = Hash[new_arr]

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

So when we sort a hash, it gives us an array of arrays with the items sorted by the original keys. Then we sort that array of arrays by the second value of each, and since it's a lazy sort, it only shifts them if need be. Then we can send it back to a hash. I'm sure there's a trick way to do a two-pass sort in one line but this seems pretty simple.

所以当我们对哈希进行排序时,它会给我们一个数组,数组中的元素按原始键排序。然后我们按每个数组的第二个值对数组进行排序,因为这是一种惰性排序,所以只有在需要的时候才移动它们。然后我们可以把它发送回散列。我相信有一种技巧可以在一行中进行二遍排序,但这看起来很简单。

#3


0  

As you already know few methods of ruby for sorting that you have used. So I would not explain it you in detail rather keep it very simple one liner for you. Here is your answer:

正如您已经知道的,用于排序的ruby方法很少。所以我不会给你们详细的解释,而是给你们写一个简单的句子。这是你的回答:

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

Thanks

谢谢

#1


5  

In many languages, Hashes/Dicts aren't ordered, because of how the are implemented under the covers. Ruby 1.9+ is nice enough to guarantee ordering.

在许多语言中,散列/命令不是有序的,因为它们是在幕后实现的。Ruby 1.9+足够好,可以保证订购。

You can do this in a single pass - Ruby allows you to sort by arbitrary criteria.

您可以在一次遍历中完成此操作——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 will return an array of [k, v] pairs, but those k, v pairs can be sorted by any criteria you want in a single pass. Once we have the sorted pairs, we turn it back into a Hash with Array#to_h (Ruby 2.1+), or you can use Hash[sorted_result] in earlier versions, as Beartech points out.

散列#sort将返回一个[k, v]对的数组,但是这些k, v对可以根据您想要的任何条件在单遍中排序。一旦我们有了排序对,我们就把它转换成一个带有#to_h (Ruby 2.1+)数组的散列,或者您可以在早期版本中使用散列[sorted_result], Beartech指出。

You could get as complex as you want in the sort block; if you're familiar with Javascript sorting, Ruby actually works the same here. The <=> method returns -1, 0, or 1 depending on how the objects compare to each other. #sort just expects one of those return values, which tells it how the two given values relate to each other. You don't even have to use <=> at all if you don't want to - something like this is equivalent to the more compact form:

你可以在排序块中得到你想要的最复杂的东西;如果您熟悉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

As you can see, as long as you always return something in the set (-1 0 1), your sort function can do whatever you want, so you can compose them however you'd like. However, such verbose forms are practically never necessary in Ruby, because of the super handy <=> operator!

正如您所看到的,只要您始终返回集合中的某些内容(-1 0 1),您的排序函数可以做任何您想做的事情,因此您可以按照自己的意愿组合它们。然而,这种冗长的表单在Ruby中实际上是没有必要的,因为超级方便的<=>操作符!

As Stefan points out, though, you have a BIG shortcut here: Array#<=> is nice enough to compare each entry between the compared arrays. This means that we can do something like:

正如Stefan指出的,这里有一个很大的快捷方式:数组#<=>非常适合比较数组之间的每个条目。这意味着我们可以做如下事情:

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

This takes each [k, v] pair, reverses it into [v, k], and uses Array#<=> to compare it. Since you need to perform this same operation on each [k, v] pair compared, you can shortcut it even further with #sort_by

这将获取每个[k, v]对,并将其转换为[v, k],并使用数组#<=>对其进行比较。由于您需要对每个[k, v]对执行相同的操作,所以您可以使用#sort_by进一步简化它

pets_ages.sort_by {|k, v| [v, k] }.to_h

What this does is for each hash entry, it passes the key and value to the block, and the return result of the block is what is used to compare this [k, v] pair to other entries. Since comparing [v, k] to another [v, k] pair will give us the result we want, we just return an array consisting of [v, k], which sort_by collects and sorts the original [k, v] pairs by.

对于每个散列条目,它将键和值传递给块,块的返回结果就是用来将这个[k, v]对与其他条目进行比较的结果。由于将[v, k]与另一个[v, k]对进行比较会得到我们想要的结果,所以我们只返回一个由[v, k]组成的数组,sort_by收集并对原始的[k, v]对进行排序。

#2


1  

As Philip pointed out, hashes were not meant to preserve order, though I think in the latest Ruby they might. But let's say they don't. Here's an array based solution that could then be re-hashed:

正如Philip所指出的,散列并不是为了保持秩序,尽管我认为在最新的Ruby中它们可能是这样的。但假设他们没有。这里有一个基于数组的解决方案,然后可以重新散列:

Edit here it is in a one-liner:

编辑在这里,它是一个一行:

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

previous answer:

之前的回答:

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]]

And finally to get a hash back:

最后,为了得到一个散列:

h = Hash[new_arr]

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

So when we sort a hash, it gives us an array of arrays with the items sorted by the original keys. Then we sort that array of arrays by the second value of each, and since it's a lazy sort, it only shifts them if need be. Then we can send it back to a hash. I'm sure there's a trick way to do a two-pass sort in one line but this seems pretty simple.

所以当我们对哈希进行排序时,它会给我们一个数组,数组中的元素按原始键排序。然后我们按每个数组的第二个值对数组进行排序,因为这是一种惰性排序,所以只有在需要的时候才移动它们。然后我们可以把它发送回散列。我相信有一种技巧可以在一行中进行二遍排序,但这看起来很简单。

#3


0  

As you already know few methods of ruby for sorting that you have used. So I would not explain it you in detail rather keep it very simple one liner for you. Here is your answer:

正如您已经知道的,用于排序的ruby方法很少。所以我不会给你们详细的解释,而是给你们写一个简单的句子。这是你的回答:

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

Thanks

谢谢