我有一个rails模型,用户想要排序的主要字段是以点符号格式存储为字符串的行项目(即:2.1.4
,2.1.4.1
,{ {1}}等)。按字母顺序排序很有效,但2.1.4.5
按字母顺序排在2.1.4.10
之前。我想称之为'基于点的数字顺序'会在2.1.4.2
之后放置2.1.4.10
,而2.1.4.9
会先于2.4.1.10.1
问题是:根据“基于点的数字顺序”,什么是Rails Way™在模型上设置默认顺序,以便行项目以正确的顺序显示。
假设一个简单的案例:
2.4.1.11
并且:line_item是一个字符串。
答案 0 :(得分:0)
我假设您使用的是PostgreSQL,如果真的想要为您的模型设置默认顺序,请将此default_scope
添加到您的LineItem
模型中:
default_scope -> { order("STRING_TO_ARRAY(line_item, '.')::int[] ASC") }
否则我建议您使用命名范围,它可以覆盖和链接:
scope :version_order, -> { order("STRING_TO_ARRAY(line_item, '.')::int[] ASC") }
答案 1 :(得分:0)
lines = ['3.3.3.3', '3.54.3.3', '3.3.3.20']
sorted = lines.sort do |a, b|
a.split('.').zip(b.split('.')).inject(0) do |res, val|
(res == 0)? val[0].to_i <=> val[1].to_i : res
end
end #=> ["3.3.3.3", "3.3.3.20", "3.54.3.3"]
要排序,我们传递一个数组和一个块,这些块为我们提供了两个在列表中彼此相邻的参数,我们可以返回0,-1或1,它告诉Ruby要交换哪个方向号。
[4,3,-1,2].sort do |x, y|
if x > y
1
elsif x < y
-1
else
0
end
end #=> [-1, 2, 3, 4]
Ruby为我们提供了一个不错的运算符,而不是那么长的逻辑:<=>
。零表示无变化,-1表示按升序排序,1表示两个数字按降序排列。 Ruby重复了一堆,并对列表进行排序。
4 <=> 4 #=> 0
3 <=> 5 #=> -1
5 <=> 3 #=> 1
7 <=> -1 #-> 1
因此,我们应该提供更高的项目(以点数为单位)优先级:
#Pseudo Code:
33.44 > 22.55 #=> true
33.44 < 44.33
通过所有数字进行整合的最简单方法是#inject
,它会为您提供值以及您所在的项目。你可以这样做:
[4,4,4].inject(0) {|sum, i| sum + i} #=> 12
[4,4,4].inject(0) {|sum, i| sum - i} #=> -12
['Hello', "I'm penne12"] {|new_word, word| new_word + "-" + word} #=> "Hello-I'm penne12"
因此,我们将使用内联:
(true)? "it's true" : "true is now false. Yay!" #=> "it's true"
(4 > 5)? "logic is weird" : "4 > 5" #=> "4 > 5"
像这样:
.inject(0) do |res, val|
(res == 0)? val[0].to_i <=> val[1].to_i : res
end
我们将.
拆分两个字符串,以获取列表:
"Hello. This. Is. A. Test.".split('.') #=> ["Hello", " This", " Is", " A", "Test"]
"4.4.4.4" #=> [4,4,4,4]
使用ruby&#39; s #Zip
按元素加入两个列表(这真的很奇怪。)
[4,4,4,4].zip([5,5,5,5]) #=> [[4,5], [4,5], [4,5], [4,5]]
如果要按其他属性排序,可以更改项目a和b。 Ruby并不关心你对变量做什么,它只关心返回值。
a, b = a.line_item, b.line_item
class LineItem < ActiveRecord::Base
validates :line_item, :presence => true, :uniqueness => true
def self.sort_by_dbno
self.all.sort do |a, b|
a, b = a.line_item, b.line_item
a.split('.').zip(b.split('.')).inject(0) do |res, val|
(res == 0)? val[0].to_i <=> val[1].to_i : res
end
end
end
end
答案 2 :(得分:-1)
我使用@ Penne12代码覆盖<=>
运算符:
def <=>(y)
self.line_item.split('.').zip(y.line_item.split('.')).inject(0) do |res, val|
(res == 0)? val[0].to_i <=> val[1].to_i : res
end
end
排序适用于任何可枚举的集合,没有排序块:
bobs_items = LineItem.where(:owner => bob, :complete => false)
"Bob's workload: #{bobs_items.sort.map { |li| li.line_item }.join(', ')}"