假设我有:
a = ["apple", "pear", ["grapes", "berries"], "peach"]
我想按以下方式排序:
a.sort_by do |f|
f.class == Array ? f.to_s : f
end
我得到:
[["grapes", "berries"], "apple", "peach", "pear"]
我实际上希望按字母顺序排列项目,数组项目按其第一个元素排序:
["apple", ["grapes", "berries"], "peach", "pear"]
或者,最好是我想要:
["apple", "grapes, berries", "peach", "pear"]
如果示例不够清楚,我希望按字母顺序对项目进行排序。
关于如何到达那里的任何建议?
到目前为止,我已经尝试了一些方法,但似乎无法到达那里。谢谢。
我认为这就是你想要的:
a.sort_by { |f| f.class == Array ? f.first : f }
我会做的
a = ["apple", "pear", ["grapes", "berries"], "peach"]
a.map { |e| Array(e).join(", ") }.sort
# => ["apple", "grapes, berries", "peach", "pear"]
Array#sort_by 显然是正确的方法,但这里有一个关于如何在这里使用 Array#sort 的提醒:
a.sort do |s1,s2|
t1 = (s1.is_a? Array) ? s1.first : s1
t2 = (s2.is_a? Array) ? s2.first : s2
t1 <=> t2
end.map {|e| (e.is_a? Array) ? e.join(', ') : e }
#=> ["apple", "grapes, berries", "peach", "pear"]
@theTinMan指出sort
比这里的sort_by
慢得多,并给出了解释原因的参考。 我一直想看看如何使用基准测试模块,所以借此机会比较了手头问题的两种方法。 我用@Rafa的解决方案sort_by
,我的sort
。为了进行测试,我提前构建了一个包含 100 个随机样本的数组(每个样本都有 10,000 个要排序的随机元素),因此基准测试不包括构建样本所需的时间(这并非微不足道)。 10,000 个元素中有 8,000 个是 8 个小写字母的随机字符串。 其他2,000个元素是[str1, str2]
形式的2元组,其中str1
和str2
分别是8个小写字母的随机字符串。 我用其他参数进行了基准测试,但底线结果没有显着变化。
require 'benchmark'
# n: total number of items to sort
# m: number of two-tuples [str1, str2] among n items to sort
# n-m: number of strings among n items to sort
# k: length of each string in samples
# s: number of sorts to perform when benchmarking
def make_samples(n, m, k, s)
s.times.with_object([]) { |_, a| a << test_array(n,m,k) }
end
def test_array(n,m,k)
a = ('a'..'z').to_a
r = []
(n-m).times { r << a.sample(k).join }
m.times { r << [a.sample(k).join, a.sample(k).join] }
r.shuffle!
end
# Here's what the samples look like:
make_samples(6,2,4,4)
#=> [["bloj", "izlh", "tebz", ["lfzx", "rxko"], ["ljnv", "tpze"], "ryel"],
# ["jyoh", "ixmt", "opnv", "qdtk", ["jsve", "itjw"], ["pnog", "fkdr"]],
# ["sxme", ["emqo", "cawq"], "kbsl", "xgwk", "kanj", ["cylb", "kgpx"]],
# [["rdah", "ohgq"], "bnup", ["ytlr", "czmo"], "yxqa", "yrmh", "mzin"]]
n = 10000 # total number of items to sort
m = 2000 # number of two-tuples [str1, str2] (n-m strings)
k = 8 # length of each string
s = 100 # number of sorts to perform
samples = make_samples(n,m,k,s)
Benchmark.bm('sort_by'.size) do |bm|
bm.report 'sort_by' do
samples.each do |s|
s.sort_by { |f| f.class == Array ? f.first : f }
end
end
bm.report 'sort' do
samples.each do |s|
s.sort do |s1,s2|
t1 = (s1.is_a? Array) ? s1.first : s1
t2 = (s2.is_a? Array) ? s2.first : s2
t1 <=> t2
end
end
end
end
user system total real
sort_by 1.360000 0.000000 1.360000 ( 1.364781)
sort 4.050000 0.010000 4.060000 ( 4.057673)
虽然从来没有怀疑过,但@theTinMan是对的! 我用不同的参数做了其他一些运行,但sort_by
一直以类似的性能比重击sort
。
请注意,sort_by
的"系统"时间为零。 在其他运行中,有时sort
为零。 这些值总是零或0.010000
,导致我想知道那里发生了什么。(我在Mac上运行了这些。
对于不熟悉Benchmark
的读者,Benchmark#bm 采用的参数等于标题行所需的左填充量 (user system...
)。bm.report
将行标签作为参数。
你真的很接近。 只需.to_s
切换到.first
.
irb(main):005:0> b = ["grapes", "berries"]
=> ["grapes", "berries"]
irb(main):006:0> b.to_s
=> "["grapes", "berries"]"
irb(main):007:0> b.first
=> "grapes"
这是一个有效的:
a.sort_by do |f|
f.class == Array ? f.first : f
end
收益 率:
["apple", ["grapes", "berries"], "peach", "pear"]
a.map { |b| b.is_a?(Array) ? b.join(', ') : b }.sort
# => ["apple", "grapes, berries", "peach", "pear"]
将to_s
替换为join
。
a.sort_by do |f|
f.class == Array ? f.join : f
end
# => ["apple", ["grapes", "berries"], "peach", "pear"]
或者更简洁地说:
a.sort_by {|x| [*x].join }
# => ["apple", ["grapes", "berries"], "peach", "pear"]
to_s
的问题在于它将您的数组转换为以"["
开头的字符串:
"["grapes", "berries"]"
按字母顺序排列在其余字符串之前。
join
实际上会创建您希望按以下方式排序的字符串:
"grapesberries"
根据您的逻辑,它按字母顺序正确排列。
如果您不希望数组保持数组,则操作略有不同,但您仍将使用join
.
a.map {|x| [*x].join(", ") }.sort
# => ["apple", "grapes, berries", "peach", "pear"]
对扁平数组进行排序
如果您只想展平嵌套数组的所有元素,然后按字母顺序排序,您需要做的就是展平和排序。例如:
["apple", "pear", ["grapes", "berries"], "peach"].flatten.sort
#=> ["apple", "berries", "grapes", "peach", "pear"]