Functionally find the mapping of the first value that passes the test

In Ruby, I have an array of simple values โ€‹โ€‹(possible encodings):

encodings = %w[ utf-8 iso-8859-1 macroman ] 

I want to keep reading the file from disk until the results are valid. I could do this:

 good = encodings.find{ |enc| IO.read(file, "r:#{enc}").valid_encoding? } contents = IO.read(file, "r:#{good}") 

... but, of course, this is stupid, as it reads the file twice for good encoding. I could program it in a crude procedural style as follows:

 contents = nil encodings.each do |enc| if (s=IO.read(file, "r:#{enc}")).valid_encoding? contents = s break end end 

But I want a functional solution. I could do it so functionally:

 contents = encodings.map{|e| IO.read(f, "r:#{e}")}.find{|s| s.valid_encoding? } 

... but, of course, it saves files for each encoding, even if the first was valid.

Is there a simple template that is functional, but does not continue to read the file after the first success?

+2
source share
4 answers

If you cover lazy there, map will consume only those array elements that are used by find - that is, once find stops, map stops. So this will do what you want:

 possible_reads = encodings.lazy.map {|e| IO.read(f, "r:#{e}")} contents = possible_reads.find {|s| s.valid_encoding? } 
+4
source

Switching to sepp2k Answer: if you cannot use 2.0, lazy enums can be easily implemented in version 1.9:

 class Enumerator def lazy_find self.class.new do |yielder| self.each do |element| if yield(element) yielder.yield(element) break end end end end end a = (1..100).to_enum p a.lazy_find { |i| i.even? }.first # => 2 
+1
source

You want to use the break statement:

 contents = encodings.each do |e| s = IO.read( f, "r:#{e}" ) s.valid_encoding? and break s end 
+1
source

The best I can come up with is our good friend inject :

 contents = encodings.inject(nil) do |s,enc| s || (c=File.open(f,"r:#{enc}").valid_encoding? && c end 

This is still not optimal enough, because he continues to scroll through the encodings after searching for a match, although he does nothing with them, so this is a slight ugliness. Most of the ugliness comes from ... well, the code itself.: /

0
source

All Articles