tags:

views:

170

answers:

6

The title really really doesn't explain things. My situation is that I would like to read a file and put the contents into a hash. Now, I want to make it clever, I want to create a loop that opens every file in a directory and put it into a hash. Problem is I don't know how to assign a name relative to the file name. eg:

hash={}  
Dir.glob(path + "*") do |datafile|
  file = File.open(datafile)
  file.each do |line|
    key, value = line.chomp("\t")
    # Problem here is that I wish to have a different
    # hash name for every file I loop through
    hash[key]=value
  end
  file.close
end

Is this possible?

A: 

Can't you just do the following?

filehash = {} # after the File.open line

...
# instead of hash[key] = value, next two lines
hash[datafile] = filehash
filehash[key] = value
Vinay Sajip
+1  A: 

You want to dynamically create variables with the names of the files you process?

try this:

Dir.glob(path + "*") do |fileName|
  File.open(fileName) {

    # the variable `hash` and a variable named fileName will be
    # pointing to the same object... 
    hash = eval("#{fileName} = Hash.new")

    file.each do |line|
      key, value = line.chomp("\t")
      hash[key]=value
    end
  }
end

Of course you would have to make sure you rubify the filename first. A variable named "bla.txt" wouldn't be valid in ruby, neither would "path/to/bla.csv"

+1 for solving the problem in the requested way
bobDevil
Note though that you won't be able to access the newly created variables without eval. Also filenames containing spaces or quotes (among other things) would break this approach.
sepp2k
I'm not sure "solving the file the requested way" is necessarily a virtue. If the questioner knew enough to be certain what the best way was, he probably wouldn't be here.
Chuck
@Chuck how else would you do it? Please Educate me! Thanks
chutsu
I'm not chuck, but I would do it the way jug proposed, because a) it works with all filenames and b) you don't need eval to access the hashes afterwards.
sepp2k
Yep, I like jug's solution better. I'm not voting this down or anything — I just think this may not be the best of all possible solutions.
Chuck
Also, eval with external data is super dangerous. Say, if somebody manages to stick a file called "``rm -rf /` `;a" in the directory you're iterating, look at what gets eval'ed. So you have to do a lot of sanitizing to make this safe, and it's really important to make absolutely sure you get it right, and it's just a lot of pain that you don't need to go through since you can safely store arbitrary strings in a Hash.
Chuck
A: 

You may want to use something like this:

hash[file] = {}
hash[file][key] = value
collimarco
+8  A: 

Why don't you use a hash whose keys are the file names (in your case "datafile") and whose value are hashes in which you insert your data?

hash = Hash.new { |h, key| h[key] = Hash.new }  
Dir.glob(path + '*') do |datafile|
  next unless File.stat(datafile).file?
  File.open(datafile) do |file|
    file.each do |line|
      key, value = line.split("\t")
      puts key, value
      # Different hash name for every file is now hash[datafile]
      hash[datafile][key]=value
    end
  end
end
jug
You can replace `File.open` and `file.each` with `File.foreach(datafile) do |line|`
sepp2k
+1  A: 

If you want to create a dynamic variable, you can also use #instance_variable_set (assuming that instance variables are also OK.

Dir.glob(path + "*") do |datafile|               
  file = File.open(datafile)                     
  hash = {}                                      
  file.each do |line|                            
    key, value = line.chomp("\t")                
    hash[key] = value                            
  end                                            
  instance_variable_set("@file_#{File.basename(datafile)}", hash)    
end

This only works when the filename is a valid Ruby variable name. Otherwise you would need some transformation.

jgre
A: 

Two hashes is enough now. fileHash -> lineHash -> content.

Qianjigui