使用字符串轮廓表示法订购导轨模型

时间:2015-11-23 03:26:51

标签: ruby-on-rails ruby postgresql activerecord

我有一个rails模型,用户想要排序的主要字段是以点符号格式存储为字符串的行项目(即:2.1.42.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是一个字符串。

3 个答案:

答案 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(', ')}"