Home > Software design >  Filter a ruby array based on values in another array
Filter a ruby array based on values in another array

Time:11-11

I have an array of extensions and an array of file names:

exts = ['.zip', '.tgz', '.sql']
files = ['file1.txt', 'file2.doc', 'file2.tgz', 'file3.sql', 'file6.foo', 'file4.zip']

I want to filter the file names by one or more matching extensions. In this case, the result would be:

["file1.zip", "file2.tgz", "file3.sql", "file4.zip"]

I know I can do this with a nested loop:

exts.each_with_object([]) do |ext, arr|
    entries.each do |entry| 
        arr << entry if entry.include?(ext)
    end
end

This feels ugly to me. With select, I can avoid the feeling of nested loops:

entries.select { |entry| exts.each { |ext| entry.include?(ext) } }

This works and feels better. Is there still a more elegant way that I'm missing?

CodePudding user response:

I would use Enumerable#grep with a regexp like this:

exts = ['.zip', '.tgz', '.sql']
files = ['file1.txt', 'file2.doc', 'file2.tgz', 'file3.sql', 'file6.foo', 'file4.zip']

files.grep(/#{Regexp.union(exts)}$/)
#=> ["file2.tgz", "file3.sql", "file4.zip"]

I use Regexp.union instead of a simple exts.join('|') because exts include dots (.) which have a special meaning in regular expressions. Regexp.union escapes those dots automatically.

CodePudding user response:

Thinking about it a bit further, I realized I could make the select better if I changed the logic slightly:

entries.select{ |entry| exts.include?(entry) }

As far as I can tell, this is as clean as I can get.

  • Related