Ruby:按字母顺序对字符串数组进行排序,其中包括一些字符串数组



假设我有:

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元组,其中str1str2分别是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"]

最新更新