按降序排序字符串的方法(在复杂键中)

时间:2016-03-06 05:35:33

标签: ruby string sorting reverse

为了对数组 Error: java.io.IOException: Response code: 400 Error response: {"name":"TRANSACTION_REFUSED","message":"The request was refused. {0}","information_link":"https://developer.paypal.com/webapps/developer/docs/api/# TRANSACTION_REFUSED","debug_id":"e4334e766671"} 的字符串进行降序排序,可以使用a

reverse

但是当您想在多个排序键中使用字符串时,无法完成。假设a.sort.reverse 是包含属性itemsattr1),Stringattr2),Stringattr3的项目数组})。排序可以像:

Integer

可以通过将items.sort_by{|item| [item.attr1, item.attr2, item.attr3]} Integer相乘,为-1独立完成从上升到下降的切换:

items.sort_by{|item| [item.attr1, item.attr2, -item.attr3]}

String这种方法并不简单。可以定义这样的方法吗?如果您想对attr2进行降序排序,则应该写成:

items.sort_by{|item| [item.attr1, item.attr2.some_method, item.attr3]}

4 个答案:

答案 0 :(得分:2)

以下内容支持响应<=>的所有对象。

def generalized_array_sort(arr, inc_or_dec)
  arr.sort do |a,b|
    comp = 0
    a.zip(b).each_with_index do |(ae,be),i|
      next if (ae<=>be).zero?
      comp = (ae<=>be) * (inc_or_dec[i]==:inc ? 1 : -1)
      break
    end
    comp
  end
end

实施例

arr = [[3, "dog"], [4, "cat"], [3, "cat"], [4, "dog"]]
inc_or_dec = [:inc, :dec] 

generalized_array_sort(arr, inc_or_dec)
  #=> [[3, "dog"], [3, "cat"], [4, "dog"], [4, "cat"]]

另一个例子

class A; end
class B<A; end
class C<B; end
[A,B,C].sort #=> [C, B, A] 

arr = [[3, A], [4, B], [3, B], [4, A], [3, C], [4,C]]
inc_or_dec = [:inc, :dec] 

generalized_array_sort(arr, inc_or_dec)
  #=> [[3, A], [3, B], [3, C], [4, A], [4, B], [4, C]]

答案 1 :(得分:2)

我不确定其中任何一个都通过了您的直截了当测试,但我认为两者都能正常工作。使用@ CarySwoveland的测试数据:

arr = [[3, "dog"], [4, "cat"], [3, "cat"], [4, "dog"]]

arr.sort_by {|a, b| [ a, *b.codepoints.map(&:-@) ] }
# => [[3, "dog"], [3, "cat"], [4, "dog"], [4, "cat"]]

或者,这里的解决方案无论类型如何都有效(即它不必是字符串):

arr.sort do |a, b|
  c0 = a[0] <=> b[0]
  next c0 unless c0.zero?
  -(a[1] <=> b[1])
end
# => [[3, "dog"], [3, "cat"], [4, "dog"], [4, "cat"]]

后者可以概括为如下方法:

def arr_cmp(a, b, *dirs)
  return 0 if a.empty? && b.empty?
  return a <=> b if dirs.empty?
  a0, *a = a
  b0, *b = b
  dir, *dirs = dirs
  c0 = a0 <=> b0
  return arr_cmp(a, b, *dirs) if c0.zero?
  dir * c0
end

这就像<=>一样,但是它的最终参数采用1-1的列表来指示每个相应数组元素的排序方向,例如:

a = [3, "dog"]
b = [3, "cat"]
arr_cmp(a, b, 1, 1) # => 1
arr_cmp(a, b, 1, -1) # => -1

<=>类似,它在sort块中最有用:

arr.sort {|a, b| arr_cmp(a, b, 1, -1) }
# => [[3, "dog"], [3, "cat"], [4, "dog"], [4, "cat"]]

尽管如此,我还没有对它进行过多次测试,因此可能存在失败的边缘情况。

答案 2 :(得分:2)

我认为您始终可以将字符串转换为整数数组(ord)。像这样:

strings = [["Hello", "world"], ["Hello", "kitty"], ["Hello", "darling"]]
strings.sort_by do |s1, s2|
  [
    s1,
    s2.chars.map(&:ord).map{ |n| -n }
  ]
end

PS:

正如@CarySwoveland在这里发现的是一个带有空字符串的角落案例,这可以通过这个非优雅的解决方案来解决:

strings.sort_by do |s1, s2|
  [
    s1,
    s2.chars.
       map(&:ord).
       tap{|chars| chars << -Float::INFINITY if chars.empty? }.
       map{ |n| -n }
  ]
end

并且@Jordan善意地提到sort_by使用Schwartzian Transform因此您根本不需要预处理。

答案 3 :(得分:1)

虽然我不知道通用的学术实施,但在现实生活中我会选择:

class String 
  def hash_for_sort precision = 5
    (@h_f_p ||= {})[precision] ||= self[0...precision].codepoints.map do |cp|
      [cp, 99999].min.to_s.ljust 5, '0'
    join.to_i
  end
end

现在可以按-item.attr2.hash_for_sort排序。

上述方法存在一些问题:

  • 没有对> precision字母不同的字符串进行有效排序;
  • 对该函数的初始调用是O(self.length);
  • 99999以上的代码点将被视为相等(排序不准确)。

但考虑到真正的情况,我无法想象何时这还不够。

P.S。如果我要精确地解决这个问题,我会搜索一个算法,以一对一的方式将字符串转换为浮点数。