使用数组键进行安全排序

时间:2019-08-15 11:11:07

标签: ruby-on-rails ruby ruby-on-rails-5

有这样的代码。在其中,按对象名称对数组进行排序。

但是麻烦的是,例如,如果不是 first_name last_name ,则为 nil,false

那是一个错误。 错误是:比较Array和Array失败。

array.sort_by do |item|
    if item.expression
      [item.title, item.description]
    else
      [item.first_name, item.last_name]
    end
  end

如何针对这种情况安全地分类?

[item.title || 0, item.description || 0]

我认为这样做不是专业的。有什么好主意吗?

2 个答案:

答案 0 :(得分:1)

(注意:我不明白您的意思是“ first_name,而last_name可能是false”。听起来不对,不是我。我要假设它们可能是nil,而不是false。)

要获得快速,简单的解决方法,您可以将nil的值强制转换为String s,例如:

array.sort_by do |item|
  if item.expression
    [String(item.title), String(item.description)]
  else
    [String(item.first_name), String(item.last_name)]
  end
end

这几乎等于 说“按字母顺序排序; nil最后一个”。

它不能区分""nil这两个值。如果要使排序的这一方面更加严格,则需要编写something more custom, using the <=> operator

答案 1 :(得分:0)

有两种方法可以做到这一点。首先,让我们创建一些数据。

Items = Struct.new(:expression, :title, :description, :first_name, :last_name)

values =
  [[false, 'x', 'x', 'Bob', nil],
   [true,  'Nine Lives', 'Cat story', 'x', 'x'],
   [false, 'x', 'x', 'Mary', 'Tuffet'],
   [false, 'x', 'x', false, 'Cocker'],
   [false, 'x', 'x', 'Lois', 'Lane'],
   [false, 'x', 'x', 'Bob', 'Newhart'],
   [true,  'Death Comes Calling', 'Who done it', 'x', 'x'],
   [false, 'x', 'x', 'Mary', 'Martin']]

array = values.map { |a| Items.new(*a) }

注意:

array.map do |item|
  [item.expression, item.title, item.description, item.first_name, item.last_name]
end == values
  #=> true

分区,排序和连接

goodies, baddies = array.partition { |item|
  item.expression || (item.first_name && item.last_name) }

a = goodies.sort_by do |item| item.expression ? [item.title, item.description] :
      [item.first_name, item.last_name]
    end.concat(baddies)

a.map do |item|
  [item.expression, item.title, item.description, item.first_name, item.last_name]
end
  #=> [[false, "x", "x", "Bob", "Newhart"],
  #    [true, "Death Comes Calling", "Who done it", "x", "x"],
  #    [false, "x", "x", "Lois", "Lane"],
  #    [false, "x", "x", "Mary", "Martin"],
  #    [false, "x", "x", "Mary", "Tuffet"],
  #    [true, "Nine Lives", "Cat story", "x", "x"],
  #    [false, "x", "x", "Bob", nil],
  #    [false, "x", "x", false, "Cocker"]] 

最后两个元素放在最后。前面的元素按以下顺序排序:

["Bob", "Newhart"]
["Death Comes Calling", "Who done it"]
["Lois", "Lane"]
["Mary", "Martin"]
["Mary", "Tuffet"]
["Nine Lives", "Cat story"]

使用Array#sort

b = array.sort do |item1, item2|
  if    item1.expression==false && !(item1.first_name && item1.last_name)
    1
  elsif item2.expression==false && !(item2.first_name && item2.last_name)
    -1
  else
    (item1.expression ? [item1.title, item1.description] :
                        [item1.first_name, item1.last_name]) <=>
    (item2.expression ? [item2.title, item2.description] :
                        [item2.first_name, item2.last_name])
  end
end

b.first(6) == a.first(6)
  #=> true

ab的最后两个元素包含相同的两个元素,但顺序不同。由于未指定这些元素的顺序,因此应该没有问题。