2013-05-08 4 views
2

Допустим, у меня есть текстовый файл, как это:Ruby: массив хэшей из текстового файла

1 2 3 
4 5 6 
7 8 9 
... 

Я хочу, чтобы прочитать его в структуру данных, как это:

h[1] = { "a" => 1, "b" => 2, "c" => 3 } 
h[2] = { "a" => 4, "b" => 5, "c" => 6 } 
h[3] = { "a" => 7, "b" => 8, "c" => 9 } 

На первый взгляд кажется легким. Я использую:

lines=File.read(ARGV[0]).split("\n") 
h=[] 
lines.each (|x| h << x.split()) 

И полностью застрял в этой точке. Как преобразовать h в массив хешей?

ответ

4
lines = File.readlines(ARGV[0]) 
lines.map { |l| x = l.split(/\s/).map(&:to_i); { 'a' => x[0], 'b' => x[1], 'c' => x[2] } } 
+0

+1 хороший ответ! – alfasin

+1

Вы сделали это немного сложнее, чем нужно. 'File.readlines' уже выполняет разделение на строки новой строки (и работает в Windows). 'split' разбивается на пробелы без каких-либо аргументов. – Max

+0

Спасибо @Max. Добавляем это. Обратите внимание, что 'File.readlines' оставляет' \ n' в конце каждой строки, но 'to_i' отбрасывает его – Christian

0

Мое решение не так элегантно, как христианина:

base = ['a','b','c'] 
h=[] 
i = 0 # traverses the a,b,c 
j = 1 # traverses the lines of the hash h 

File.open(ARGV[0]).each_line do |line| 
    arr = line.split(' ') 
    arr.each do |x| 
    if !h[j].nil? 
     h[j][base[i]] = x.to_i 
    else # first entry in the hash should be set like this: 
     h[j] = {base[i] => x.to_i} 
    end 
    i += 1; 
    end 
    i = 0 
    j += 1 
end 
p h[1] 
p h[2] 
p h[3] 

выход:

{"a"=>"1", "b"=>"2", "c"=>"3"} 
{"a"=>"4", "b"=>"5", "c"=>"6"} 
{"a"=>"7", "b"=>"8", "c"=>"9"} 

Я начал с J = 1, так как это то, что просил ОП для, несмотря на то, что это будет вероятно, имеет смысл начать с нуля.

1

Существует драгоценный камень вы можете использовать для этого: smarter_csv

Поместите это в Gemfile:

gem 'smarter_csv', '1.0.5' 

, а затем:

require 'smarter_csv' 
result = SmarterCSV.process('/tmp/bla.csv', 
     {:col_sep => ' ', 
      :headers_in_file => false, 
      :user_provided_headers => ['a','b','c'], 
      :strings_as_keys => true 
     } 
) 
    => [{"a"=>1, "b"=>2, "c"=>3}, 
     {"a"=>4, "b"=>5, "c"=>6}, 
     {"a"=>7, "b"=>8, "c"=>9}] 

result[0] 
    => {"a"=>1, "b"=>2, "c"=>3} 

Смотрите также: smarter_csv README

4
def parse(filename) 
    File.readlines(filename).map do |line| 
    Hash[('a'..'c').zip(line.split.map(&:to_i))] 
    end 
end 

parse(ARGV[0]) # => [{"a"=>1, "b"=>2, "c"=>3}, {"a"=>4, "b"=>5, "c"=>6}, {"a"=>7, "b"=>8, "c"=>9}] 
+0

+1 красивый ответ! – alfasin

+0

А да, функциональный подход. – squiguy

1

Я хотел бы использовать:

require 'pp' 

ary = [] 
DATA.each_line do |li| 
    ary << Hash[%w[a b c].zip(li.split)] 
end 

pp ary 

__END__ 
1 2 3 
4 5 6 
7 8 9 

Бег, что я получаю:

[{"a"=>"1", "b"=>"2", "c"=>"3"}, 
{"a"=>"4", "b"=>"5", "c"=>"6"}, 
{"a"=>"7", "b"=>"8", "c"=>"9"}] 

Изменение ary к h, если вы хотите, чтобы ваше имя переменной.

Если вы читаете из файла, используйте File.foreach('file/to/read.txt') вместо each_line.

Смежные вопросы