How do I sort a list of versions in Ruby? I've seen stuff about natural sort, but this is a step beyond that.
如何在Ruby中对版本列表进行排序?我已经看到了关于自然排序的东西,但这是一个超越它的一步。
Input is a bunch of strings like this:
输入是一堆像这样的字符串:
input = ['10.0.0b12', '10.0.0b3', '10.0.0a2', '9.0.10', '9.0.3']
I can almost do it with the naturally gem:
我几乎可以用天然宝石做到这一点:
require 'naturally'
Naturally.sort(input)
=> ["9.0.3", "9.0.10", "10.0.0a2", "10.0.0b12", "10.0.0b3"]
Problem: 10.0.0b3 is sorted after 10.0.0b12; 10.0.0b3 should be first.
问题:10.0.0b3在10.0.0b12之后排序; 10.0.0b3应该是第一个。
Anyone have a way that works? Other languages are helpful too!
有人有办法吗?其他语言也很有帮助!
3 个解决方案
#1
20
Ruby ships with the Gem class, which knows about versions:
Ruby附带了Gem类,它知道版本:
ar = ['10.0.0b12', '10.0.0b3', '10.0.0a2', '9.0.10', '9.0.3']
p ar.sort_by { |v| Gem::Version.new(v) }
# => ["9.0.3", "9.0.10", "10.0.0a2", "10.0.0b3", "10.0.0b12"]
#2
2
If you interpret this as "sort by each segment of digits", then you the following will handle your example input above:
如果您将其解释为“按每个数字段排序”,那么您将在上面处理您的示例输入:
input.map{ |ver| ver.split(%r{[^\d]+}).map(&:to_i) }.zip(input).sort.map(&:last)
=> ["9_0", "9_1", "10_0b3", "10_0b12"]
That is,
那是,
- for each value, eg
10_0b3
- 对于每个值,例如10_0b3
- split on any length of non-digit characters, eg
["10","0","3"]
- 拆分任意长度的非数字字符,例如[“10”,“0”,“3”]
- cast each digit segment to integer, eg
[10,0,3]
- 将每个数字段转换为整数,例如[10,0,3]
- zip with original input, yields
[[[10, 0, 12], "10_0b12"], [[10, 0, 3], "10_0b3"], [[9, 0], "9_0"], [[9, 1], "9_1"]]
- zip与原始输入,产生[[[10,0,12],“10_0b12”],[[10,0,3],“10_0b3”],[[9,0],“9_0”],[[9 ,1],“9_1”]]
- sort, by virtue of
[10,0,3] < [10,0,12]
- 排序,凭借[10,0,3] <[10,0,12]
- get last value of each element, which is the original input value which corresponds to each processed sortable value
- 获取每个元素的最后一个值,这是与每个已处理的可排序值对应的原始输入值
Now granted, this is still quite custom -- version numbers as simple as "9_0a" vs "9_0b" won't be handled, both will appear to be [9,0] -- so you may need to tweak it further, but hopefully this starts you down a viable path.
现在已经批准了,这仍然非常自定义 - 版本号简单如“9_0a”vs“9_0b”将不会被处理,两者看起来都是[9,0] - 所以你可能需要进一步调整它,但是希望这能让你走上一条可行的道路。
EDIT: Example input above changed, so I changed the regex to make sure the digit-matching is greedy, and with that it still holds up:
编辑:上面的示例输入已更改,因此我更改了正则表达式以确保数字匹配是贪婪的,并且它仍然保持:
irb(main):018:0> input = ['10.0.0b12', '10.0.0b3', '9.0.10', '9.0.3']
=> ["10.0.0b12", "10.0.0b3", "9.0.10", "9.0.3"]
irb(main):025:0> input.map{ |ver| ver.split(%r{[^\d]+}).map(&:to_i) }.zip(input).sort.map(&:last)
=> ["9.0.3", "9.0.10", "10.0.0b3", "10.0.0b12"]
#3
0
In the specific case that you are working with NuGet and want to parse, compare or sort by NuGet's peculiar own versioning scheme from Ruby code, there is now this:
在特定情况下,您正在使用NuGet并希望通过NuGet的Ruby代码中特有的自己的版本控制方案进行解析,比较或排序,现在有:
https://rubygems.org/gems/nuget_versions
https://rubygems.org/gems/nuget_versions
I created it specifically to solve this problem. NuGet's version numbers are a bit weird, they are a superset of SemVer that also permits the use of 4 components instead of 3.
我专门创建它来解决这个问题。 NuGet的版本号有点奇怪,它们是SemVer的超集,它也允许使用4个组件而不是3个。
#1
20
Ruby ships with the Gem class, which knows about versions:
Ruby附带了Gem类,它知道版本:
ar = ['10.0.0b12', '10.0.0b3', '10.0.0a2', '9.0.10', '9.0.3']
p ar.sort_by { |v| Gem::Version.new(v) }
# => ["9.0.3", "9.0.10", "10.0.0a2", "10.0.0b3", "10.0.0b12"]
#2
2
If you interpret this as "sort by each segment of digits", then you the following will handle your example input above:
如果您将其解释为“按每个数字段排序”,那么您将在上面处理您的示例输入:
input.map{ |ver| ver.split(%r{[^\d]+}).map(&:to_i) }.zip(input).sort.map(&:last)
=> ["9_0", "9_1", "10_0b3", "10_0b12"]
That is,
那是,
- for each value, eg
10_0b3
- 对于每个值,例如10_0b3
- split on any length of non-digit characters, eg
["10","0","3"]
- 拆分任意长度的非数字字符,例如[“10”,“0”,“3”]
- cast each digit segment to integer, eg
[10,0,3]
- 将每个数字段转换为整数,例如[10,0,3]
- zip with original input, yields
[[[10, 0, 12], "10_0b12"], [[10, 0, 3], "10_0b3"], [[9, 0], "9_0"], [[9, 1], "9_1"]]
- zip与原始输入,产生[[[10,0,12],“10_0b12”],[[10,0,3],“10_0b3”],[[9,0],“9_0”],[[9 ,1],“9_1”]]
- sort, by virtue of
[10,0,3] < [10,0,12]
- 排序,凭借[10,0,3] <[10,0,12]
- get last value of each element, which is the original input value which corresponds to each processed sortable value
- 获取每个元素的最后一个值,这是与每个已处理的可排序值对应的原始输入值
Now granted, this is still quite custom -- version numbers as simple as "9_0a" vs "9_0b" won't be handled, both will appear to be [9,0] -- so you may need to tweak it further, but hopefully this starts you down a viable path.
现在已经批准了,这仍然非常自定义 - 版本号简单如“9_0a”vs“9_0b”将不会被处理,两者看起来都是[9,0] - 所以你可能需要进一步调整它,但是希望这能让你走上一条可行的道路。
EDIT: Example input above changed, so I changed the regex to make sure the digit-matching is greedy, and with that it still holds up:
编辑:上面的示例输入已更改,因此我更改了正则表达式以确保数字匹配是贪婪的,并且它仍然保持:
irb(main):018:0> input = ['10.0.0b12', '10.0.0b3', '9.0.10', '9.0.3']
=> ["10.0.0b12", "10.0.0b3", "9.0.10", "9.0.3"]
irb(main):025:0> input.map{ |ver| ver.split(%r{[^\d]+}).map(&:to_i) }.zip(input).sort.map(&:last)
=> ["9.0.3", "9.0.10", "10.0.0b3", "10.0.0b12"]
#3
0
In the specific case that you are working with NuGet and want to parse, compare or sort by NuGet's peculiar own versioning scheme from Ruby code, there is now this:
在特定情况下,您正在使用NuGet并希望通过NuGet的Ruby代码中特有的自己的版本控制方案进行解析,比较或排序,现在有:
https://rubygems.org/gems/nuget_versions
https://rubygems.org/gems/nuget_versions
I created it specifically to solve this problem. NuGet's version numbers are a bit weird, they are a superset of SemVer that also permits the use of 4 components instead of 3.
我专门创建它来解决这个问题。 NuGet的版本号有点奇怪,它们是SemVer的超集,它也允许使用4个组件而不是3个。