You can do this with a simple one-liner that avoids using end_with?
:
Dir[Dir.pwd + '/*.{flac,wv}'].each { |filename| pp filename }
For your question about what values are accepted by end_with?
, the answer is that it accepts a comma-delimited list of values, e.g.:
'foo'.end_with?('a', 'b', 'c')
=> false
'foo'.end_with?('a', 'b', 'c', 'o')
=> true
The documentation example of end_with?([suffixes]+) → true or false
can be read as "end_with?
accepts one or more suffixes delimited by commas, which will be treated as an array by the receiving object, and if the string ends with any of the suffixes then return true
, else return false
."
You can also use the splat operator to convert an array to an acceptable value for end_with?
:
'foo'.end_with?(*['a', 'b', 'c'])
=> false
'foo'.end_with?(*['a', 'b', 'c', 'o'])
=> true
Update for clarification of examples given
Judging from the comments below, Rich_F was extremely confused by this answer about the splat operator so I will clarify my answer to help ensure that no one else is confused by the example I have provided.
The splat operator *
in Ruby will convert an array's contents to a comma-delimited list of arguments. It is often used when passing an array as an argument to a method, for example:
array = ['a', 'b', 'c']
'foo'.end_with?(*array)
This will "splat" the array into a comma delimited list of arguments, and is functionally equivalent to:
'foo'.end_with?(*['a', 'b', 'c'])
Likewise, it is functionally equivalent to:
'foo'.end_with?('a', 'b', 'c')
Likewise, it is functionally equivalent to:
array = %w(a b c)
'foo'.end_with?(*array)
Likewise, it is functionally equivalent to:
'foo'.end_with?(*%w(a b c))
Likewise, it is functionally equivalent to:
filetypes = %w(.flac .wv)
filename.end_with?(*filetypes)
Given this knowledge about how the splat operator affects arrays and the examples for passing arrays as arguments to methods, one can deduce that the given code example in the question can be altered as follows:
filetypes = %w(.flac .wv)
Dir.open(Dir.pwd).each do |filename|
pp filename if filename.end_with?(*filetypes)
end
The only change is to add *
. By adding the splat operator *
before the argument filetypes
, the filetypes
array will be expanded as comma-delimited arguments for the call to end_with?
.
This is not an efficient solution to the problem because it requires iterating over the filetypes
array once for every iteration of the directory listing and iterates over all objects in the directory regardless of matching, as opposed to using Dir[Dir.pwd + '/*.{flac,wv}'].each { |filename| pp filename }
which returns only the relevant files and does not have to iterate over a second array for each iteration. As such, using end_with?
in the example given is massively inefficient and should not be used in any real code.
For example, given a directory with 10,000 files ending in .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)
In comparison to the original example in the question, which is over twice as slow:
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)
The difference becomes much more pronounced when the number of matching files is far below the number of non-matching files. Here, the directory has 1,000 .flac
files and 9,000 .txt
files:
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)
Compared to the original example using the splat operator, which is over 36 times slower:
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) } } } }
11.182205 0.014303 11.196508 ( 11.210042)
11.154286 0.011573 11.165859 ( 11.178611)
11.081012 0.009853 11.090865 ( 11.098028)
11.084294 0.009361 11.093655 ( 11.101870)
10.990442 0.007118 10.997560 ( 11.002036)
11.044119 0.009284 11.053403 ( 11.058608)
11.072604 0.009114 11.081718 ( 11.087941)
11.127151 0.009354 11.136505 ( 11.143270)
11.172101 0.012262 11.184363 ( 11.195138)
11.126791 0.010767 11.137558 ( 11.145617)