我有一个数组数组:
x = [
["ready", 5], ["shipped", 1], ["pending", 1], ["refunded", 1],
["delivered", 23], ["scheduled", 1], ["canceled", 51]
]
我的排序数组是
order_array = [
"ready", "in_progress", "recieved", "shipped", "scheduled", "pick_up",
"delivered", "canceled", "failed", "refunded", "refund_failed"
]
我需要根据每个子阵列中第一个元素的值来订购x
。所需的排序数组是:
[
["ready", 5], ["shipped", 1], ["scheduled", 1], ["delivered", 23],
["canceled", 51], ["refunded", 1]
]
使用sort_by
不会导致所需的排序,它会导致相同的数组。
result = x.sort_by {|u| order_array.index(u)}
# => [
# ["ready", 5], ["shipped", 1], ["pending", 1], ["refunded", 1],
# ["delivered", 23], ["scheduled", 1], ["canceled", 51]
# ]
h = x.to_h
# => {"ready"=>5,
# "shipped"=>1,
# "pending"=>1,
# "refunded"=>1,
# "delivered"=>23,
# "scheduled"=>1,
# "canceled"=>51}
order_array.map{|key| [key, h[key]] if h.key?(key)}.compact
# => [["ready", 5],
# ["shipped", 1],
# ["scheduled", 1],
# ["delivered", 23],
# ["canceled", 51],
# ["refunded", 1]]
要么
h = x.to_h{|k, v| [k, [k, v]]}
#=> {"ready"=>["ready", 5],
# "shipped"=>["shipped", 1],
# "pending"=>["pending", 1],
# "refunded"=>["refunded", 1],
# "delivered"=>["delivered", 23],
# "scheduled"=>["scheduled", 1],
# "canceled"=>["canceled", 51]}
order_array.map{|k| h[k]}.compact
#=> [["ready", 5],
# ["shipped", 1],
# ["scheduled", 1],
# ["delivered", 23],
# ["canceled", 51],
# ["refunded", 1]]
要么
h = x.to_h{|k, v| [k, [k, v]]}
#=> {"ready"=>["ready", 5],
# "shipped"=>["shipped", 1],
# "pending"=>["pending", 1],
# "refunded"=>["refunded", 1],
# "delivered"=>["delivered", 23],
# "scheduled"=>["scheduled", 1],
# "canceled"=>["canceled", 51]}
h.values_at(*order_array).compact
#=> [["ready", 5],
# ["shipped", 1],
# ["scheduled", 1],
# ["delivered", 23],
# ["canceled", 51],
# ["refunded", 1]]
assoc似乎很有帮助:“搜索一个数组,其元素也是使用obj。==将obj与每个包含数组的第一个元素进行比较的数组。”
order_array.map{|e| x.assoc(e) }.compact
你几乎就在那里:index
没有工作,因为你正在比较完整的数组,而不是它的第一个元素。这将有效:
result = x.sort_by { |u| order_array.index(u[0]) || 100 }
#=> [["ready", 5], ["shipped", 1], ["scheduled", 1], ["delivered", 23], ["canceled", 51], ["refunded", 1], ["pending", 1]]
请注意,如果在100
中找不到该值,则order_array
默认为排序的后面。
编辑
这最初被接受,尽管包括["pending", 1]
表明它符合要求;但是,这是一个避免不必要的输入的解决方案,如果需要,它也可以处理重复的输入。
order_array.each_with_object([]) { |ordered_by, array| array.push(*x.select { |item| item[0] == ordered_by }) }
#=> [["ready", 5], ["shipped", 1], ["scheduled", 1], ["delivered", 23], ["canceled", 51], ["refunded", 1]]
或者,尽管仍然允许每个订购商品下的重复值,但速度非常快:
hash = x.each_with_object(Hash.new { |h,k| h[k] = [] }) { |item, h| h[item[0]] << item[1] }
order_array.flat_map { |key| [key, hash[key]] }
基准
以下是具有更大数据集的此方案的基准:https://repl.it/repls/SentimentalAdequateClick。看起来像Sawa的方法一路领先,尽管如果将来有重复的值,我的最后努力也会轻而易举。此外,我的第二次努力很糟糕(这让我感到惊讶):)
我建议
x.keep_if { |e| order_array.include? e[0] }.sort_by { |e| order_array.index(e[0]) }
由于某些值不是order_array
的元素,例如"pending"
。
#=> [["ready", 5], ["shipped", 1], ["scheduled", 1], ["delivered", 23], ["canceled", 51], ["refunded", 1]]
500.times
:
# user system total real
# sawa 0.006698 0.000132 0.006830 ( 0.006996) # on the first method
# ray 0.005543 0.000123 0.005666 ( 0.005770)
# igian 0.001923 0.000003 0.001926 ( 0.001927)
# srack 0.005270 0.000168 0.005438 ( 0.005540) # on the last method
xx = x.to_h # less than Ruby 2.6
order_array.each.with_object([]) { |k, res| res << [k, xx[k]] if xx.has_key? k }
你可以尝试下面的代码来有效地找到输出,
order_array.map { |p| x.detect { |y| y[0] == p } }.compact
# => [["ready", 5], ["shipped", 1], ["scheduled", 1], ["delivered", 23], ["canceled", 51], ["refunded", 1]]
我假设:
x
每个元素的第一个元素不一定是唯一的;x
的所有元素,其第一个元素相同,其第一个元素是order_array
的成员,按照x
中这些元素出现的顺序连续出现在返回的(已排序的)数组中;x
的第一个元素不是order_array
成员的任何元素出现在返回的(排序的)数组中,所有元素的第一个元素都在sorted_array
中,并且所有这些元素都出现在返回的数组中(在结尾处),顺序是它们发生在x
;和x = [
["ready", 5], ["shipped", 1], ["pending", 1], ["refunded", 1], ["originated", 3],
["delivered", 23], ["scheduled", 1], ["ready", 8], ["canceled", 51]
]
order_array = [
"ready", "in_progress", "received", "shipped", "scheduled", "pick_up",
"delivered", "canceled", "failed", "refunded", "refund_failed"
]
order_pos = order_array.each_with_object({}) { |word,h| h[word] = [] }
#=> {"ready"=>[], "in_progress"=>[], "received"=>[], "shipped"=>[],
# "scheduled"=>[], "pick_up"=>[], "delivered"=>[], "canceled"=>[],
# "failed"=>[], "refunded"=>[], "refund_failed"=>[]}
back = x.each_with_index.with_object([]) { |((word,v),i),back|
order_pos.key?(word) ? (order_pos[word] << i) : back << [word,v] }
#=> [["pending", 1], ["originated", 3]]
order_pos.flat_map { |word,offsets| offsets.map { |i| x[i] } }.concat(back)
#=> [["ready", 5], ["ready", 8], ["shipped", 1], ["scheduled", 1],
# ["delivered", 23], ["canceled", 51], ["refunded", 1], ["pending", 1],
# ["originated", 3]]
注意:
order_pos
#=> {"ready"=>[0, 7], "in_progress"=>[], "received"=>[], "shipped"=>[1],
# "scheduled"=>[6], "pick_up"==>[], "delivered"=>[5], "canceled"=>[8],
# "failed"=>[], "refunded"=>[3], "refund_failed"=>[]}
有必要初始化order_pos
,以便其密钥由order_arr
订购。这是Ruby 1.9中有争议的变化的一个例子,它保证了散列键将保持按键插入顺序。