Ruby String end_with?数组

时间:2018-10-24 20:34:23

标签: arrays ruby string

我正在寻找一种限定文件名是否在数组内以后缀结尾的方法。

filetypes = %w(.flac .wv)
Dir.open(Dir.pwd).each do |filename|
  q = "." + filename.split('.').last.to_s
  pp filename if filetypes.include?(q)
end

我认为必须有某种遍历限定符数组的内置迭代器。我在这里想念东西吗?

filetypes = %w(.flac .wv)
Dir.open(Dir.pwd).each do |filename|
  pp filename if filename.end_with?(filetypes)
end

这不起作用。文档说:

end_with?([suffixes]+) → true or false

因此,这表明可以遍历一个数组,但是它对我不起作用。如何将数组传递给end_with?

2 个答案:

答案 0 :(得分:2)

您只是缺少splat运算符(*):

filetypes = %w(.flac .wv)

Dir.open(Dir.pwd).each do |filename|
  pp(filename) if filename.end_with?(*filetypes)
end

答案 1 :(得分:0)

您可以使用一个简单的单行代码来避免使用end_with?

Dir[Dir.pwd + '/*.{flac,wv}'].each { |filename| pp filename }

对于您有关end_with?接受哪些值的问题,答案是它接受逗号分隔的值列表,例如:

'foo'.end_with?('a', 'b', 'c')
=> false
'foo'.end_with?('a', 'b', 'c', 'o')
=> true

end_with?([suffixes]+) → true or false的文档示例可以理解为“ end_with?接受一个或多个以逗号分隔的后缀,该后缀将被接收对象视为一个数组,并且如果字符串以任何结尾的后缀返回true,否则返回false。”

您还可以使用splat operator将数组转换为end_with?的可接受值:

'foo'.end_with?(*['a', 'b', 'c'])
=> false
'foo'.end_with?(*['a', 'b', 'c', 'o'])
=> true

更新以澄清给出的示例

从下面的评论来看,Rich_F对这个关于splat运算符的回答感到非常困惑,因此我将澄清我的回答,以确保我提供的示例不会让其他人感到困惑。

Ruby中的splat运算符*会将数组的内容转换为以逗号分隔的参数列表。通常在将数组作为方法的参数传递给它时使用,例如:

array = ['a', 'b', 'c']
'foo'.end_with?(*array)

这会将数组“ splat”成逗号分隔的参数列表,并且在功能上等同于:

'foo'.end_with?(*['a', 'b', 'c'])

同样,它在功能上等同于:

'foo'.end_with?('a', 'b', 'c')

同样,它在功能上等同于:

array = %w(a b c)
'foo'.end_with?(*array)

同样,它在功能上等同于:

'foo'.end_with?(*%w(a b c))

同样,它在功能上等同于:

filetypes = %w(.flac .wv)
filename.end_with?(*filetypes)

鉴于有关splat运算符如何影响数组的知识以及将数组作为方法的参数传递给示例的知识,可以推断出问题中给定的代码示例可以进行如下更改:

filetypes = %w(.flac .wv)
Dir.open(Dir.pwd).each do |filename|
  pp filename if filename.end_with?(*filetypes)
end

唯一的更改是添加*。通过在参数*之前添加splat运算符filetypesfiletypes数组将扩展为以逗号分隔的参数,以调用end_with?

这不是解决此问题的有效方法,因为它需要对目录列表的每次迭代都对filetypes数组进行一次迭代,并且对目录中的所有对象进行迭代(无论是否匹配),而不是使用{{ 1}},它仅返回相关文件,而不必每次迭代都遍历第二个数组。因此,在给出的示例中使用Dir[Dir.pwd + '/*.{flac,wv}'].each { |filename| pp filename }效率非常低,并且不应在任何实际代码中使用。

例如,给定一个目录,该目录包含10,000个以end_with?结尾的文件:

.flac

与问题中的原始示例相比,速度慢了两倍:

require 'benchmark'

dir = Dir[Dir.pwd + '/*.{flac,vw}']; nil
10.times { puts Benchmark.measure { 10000.times { dir.each { |filename| nil } } } }
  4.271809   0.001728   4.273537 (  4.274684)
  4.279765   0.002286   4.282051 (  4.283524)
  4.334877   0.004689   4.339566 (  4.343982)
  4.269334   0.001593   4.270927 (  4.272033)
  4.256148   0.001545   4.257693 (  4.258734)
  4.261371   0.001733   4.263104 (  4.264229)
  4.254568   0.001085   4.255653 (  4.256379)
  4.259886   0.001245   4.261131 (  4.261711)
  4.258024   0.001964   4.259988 (  4.261133)
  4.236385   0.001142   4.237527 (  4.238184)

当匹配文件的数量远远低于不匹配文件的数量时,差异变得更加明显。在此,目录包含1,000个require 'benchmark' dir = Dir[Dir.pwd + '/*']; nil filetypes = %w(flac vw); nil 10.times { puts Benchmark.measure { 10000.times { dir.each { |filename| nil if filename.end_with?(*filetypes) } } } } 9.509041 0.003634 9.512675 ( 9.514197) 9.484041 0.003079 9.487120 ( 9.488686) 9.508674 0.002872 9.511546 ( 9.512768) 9.508382 0.002809 9.511191 ( 9.512343) 9.762489 0.011043 9.773532 ( 9.783415) 9.607308 0.005655 9.612963 ( 9.616716) 9.962166 0.009848 9.972014 ( 9.978026) 9.621152 0.005883 9.627035 ( 9.631075) 10.811991 0.010787 10.822778 ( 10.831729) 10.461568 0.013571 10.475139 ( 10.487688) 文件和9,000个.flac文件:

.txt

与使用splat运算符的原始示例相比,它慢了 36倍

require 'benchmark'

dir = Dir[Dir.pwd + '/*.{flac,vw}']; nil
10.times { puts Benchmark.measure { 10000.times { dir.each { |filename| nil } } } }
  0.384366   0.000210   0.384576 (  0.384669)
  0.384336   0.000186   0.384522 (  0.384717)
  0.386674   0.000178   0.386852 (  0.386947)
  0.383575   0.000075   0.383650 (  0.383671)
  0.382555   0.000090   0.382645 (  0.382692)
  0.384618   0.000048   0.384666 (  0.384677)
  0.384687   0.000199   0.384886 (  0.384976)
  0.386517   0.000193   0.386710 (  0.386842)
  0.386167   0.000132   0.386299 (  0.386388)
  0.390683   0.000093   0.390776 (  0.390817)