基于多个键/值对的自定义排序哈希数组

时间:2015-11-06 20:49:40

标签: ruby sorting

我有一个哈希数组,我需要根据两个不同的键值对进行排序。

这是我要排序的数组:

array_group =  [
 {operator: OR, name: "some string", status: false},
 {operator: AND, name: "other string", status: false},
 {operator: _NOT_PRESENT, name: "another string", status: true},
 {operator: AND, name: "just string", status: true}
]

我想对array_group进行排序,因此我首先包含status: true的项目,然后是status: false,然后是operator: _NOT_PRESENT的项目,最后根据名称对其进行排序,结果如下:

array_group = [
 {operator: AND, name: "just string", status: true},
 {operator: AND, name: "other string", status: false},
 {operator: OR, name: "some string", status: false},
 {operator: _NOT_PRESENT, name: "another string", status: true},
]

有没有一种方法可以在不创建子数组并对其进行排序并将它们连接起来的情况下完成此操作?

4 个答案:

答案 0 :(得分:5)

您也可以使用Enumerable#sort_by。该示例构建一个数组,在排序时逐个元素进行比较。

array_group.sort_by { |e| [e[:operator] == "_NOT_PRESENT" ? 1 : 0, 
                           e[:status] ? 0 : 1, 
                           e[:name]] }

上面的示例通过operator: "_NOT_PRESENT":status的记录进行排序。以下代码段精确地执行了问题的排序。

def priority(h)
  case
  when h[:operator] == "_NOT_PRESENT" then 3
  when h[:status] == false            then 2
  # h[:status] == true
  else 1
  end
end

array_group.sort_by { |e| [priority(e), e[:name]] }

答案 1 :(得分:1)

您可以使用Array.sort方法。它接受一个带有两个参数(x,y)的块,当x大于y时它应返回1,否则为-1,如果它们相等则为0。

代码:

OR = "OR"
AND = "AND"
_NOT_PRESENT = "_NOT_PRESENT"

array_group =  [
 {operator: OR, name: "some string", status: false},
 {operator: AND, name: "other string", status: true},
 {operator: _NOT_PRESENT, name: "another string", status: true},
 {operator: AND, name: "just string", status: true}
]

results = array_group.sort do |x, y|
  next x[:operator] == _NOT_PRESENT ? 1 : -1 if x[:operator] == _NOT_PRESENT || y[:operator] == _NOT_PRESENT
  next x[:status] ? -1 : 1 if x[:status] != y[:status]
  next x[:name] <=> y[:name]
end

顺便说一下,你的输入和输出数据彼此不匹配 - 输入中OR的哈希是false,输出中是true

我相信你的输出应该如下:

[{:operator=>"AND", :name=>"just string", :status=>true},
 {:operator=>"AND", :name=>"other string", :status=>true},
 {:operator=>"OR", :name=>"some string", :status=>false},
 {:operator=>"_NOT_PRESENT", :name=>"another string", :status=>true}]

该输出实际上与您的排序逻辑匹配。

答案 2 :(得分:0)

我假设未指定的常量ORANDNOT_PRESENT定义如下(例如):

OR = "or"
AND = "and"
NOT_PRESENT = "not present"

(常量必须以大写字母开头。_NOT_PRESENT是一个局部变量。)

使用Enumerable#sort_by时,您需要按顺序对包含三个元素的数组进行排序,这三个元素对应:status:operator:name。如果0的值:statustrue,那么这三个元素中的第一个是最小的(例如1},如果:status的值是false,那么如果是其他任何内容,则2和最大(例如def rearrange(arr) arr.sort_by do |h| [ h[:status]==true ? 0 : (h[:status]==false ? 1 : 2), (h[:operator]==NOT_PRESENT) ? 0 : 1, h[:name] ] end end array_group = [ {operator: AND, name: "just string", status: true}, {operator: OR, name: "some string", status: false}, {operator: AND, name: "other string", status: false}, {operator: NOT_PRESENT, name: "another string", status: true}, ] rearrange(array_group) #=> [{:operator=>"not present", :name=>"another string", :status=>true}, # {:operator=>"and", :name=>"just string", :status=>true}, # {:operator=>"and", :name=>"other string", :status=>false}, # {:operator=>"or", :name=>"some string", :status=>false}] )。我们有:

def get_model(n):
   return make_step(model(n-1))

def make_step(model):
   # Compute gradient and make update and return new model

这不是预期的结果,但它符合我对这个问题的理解。

答案 3 :(得分:0)

您可以使用Enumerable#sort_by使用数组来保留所需的组并按名称排序而不会丢失这些组。 例如:

[[0,0,1], [1,1,1], [0,1,0], [1,0,0], [0,1,1]].sort_by &:itself
# => [[0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 1, 1]]

使用此方法,您可以设置长度为2的数组。使用索引0按所需元素“分组”,索引1按名称排序。

OR = "OR"
AND = "AND"
_NOT_PRESENT = "_NOT_PRESENT"

array_group =  [
 { operator: OR, name: "z string", status: false },
 { operator: AND, name: "a string", status: false },
 { operator: AND, name: "z string", status: true },
 { operator: OR, name: "a string", status: true },
 { operator: _NOT_PRESENT, name: "d string", status: true },
 { operator: _NOT_PRESENT, name: "b string", status: true },
 { operator: _NOT_PRESENT, name: "c string", status: false },
 { operator: _NOT_PRESENT, name: "a string", status: false }
]

# Types of "groups" you want to keep. Greater values will be at the end
BY_NOT_PRESENT = 2
BY_STATUS_FALSE = 1
BY_STATUS_TRUE = 0

array_group.sort_by do |a|
  group = if a[:operator] == _NOT_PRESENT
            BY_NOT_PRESENT
          else
            a[:status] ? BY_STATUS_TRUE : BY_STATUS_FALSE
          end
  [group, a[:name]]
end
#=> [{:operator=>"OR", :name=>"a string", :status=>true},
#     {:operator=>"AND", :name=>"z string", :status=>true},
#     {:operator=>"AND", :name=>"a string", :status=>false}, 
#     {:operator=>"OR", :name=>"z string", :status=>false}, 
#     {:operator=>"_NOT_PRESENT", :name=>"a string", :status=>false}, 
#     {:operator=>"_NOT_PRESENT", :name=>"b string", :status=>true}, 
#     {:operator=>"_NOT_PRESENT", :name=>"c string", :status=>false}, 
#     {:operator=>"_NOT_PRESENT", :name=>"d string", :status=>true}]

我这样做,因为如果只是排序_by像[status, not_present, name]这样的数组,那么status将优先于排序,覆盖name排序,从而导致:

#=> [{:operator=>"OR", :name=>"a string", :status=>true}, 
#    {:operator=>"AND", :name=>"z string", :status=>true}, 
#    {:operator=>"AND", :name=>"a string", :status=>false}, 
#    {:operator=>"OR", :name=>"z string", :status=>false}, 
#    {:operator=>"_NOT_PRESENT", :name=>"b string", :status=>true}, 
#    {:operator=>"_NOT_PRESENT", :name=>"d string", :status=>true}, 
#    {:operator=>"_NOT_PRESENT", :name=>"a string", :status=>false}, 
#    {:operator=>"_NOT_PRESENT", :name=>"c string", :status=>false}]