#ruby #database
#ruby #База данных
Вопрос:
В качестве примера я пытаюсь объединить данные из нескольких таблиц, которые у меня есть в разных файлах, разделенных вкладками:
У меня есть таблицы:
file1.txt
a 3
b 4
c 8
d 22
e 4
file2.txt
a 10.3 -2
b 4.7 -1
c 8.9 -2
e 22.1 -1
file3.txt
b T
c F
d T
f F
g T
Я хотел бы объединить их по их общему ключу, который является первым столбцом для создания следующей таблицы:
a 3 10.3 -2
b 4 4.7 -1 T
c 8 8.9 -2 F
d 22 T
e 4 22.1 -1
f F
g T
Как я мог легко добиться этого с помощью ruby..
Ted
Комментарии:
1. если вы немного измените формат ввода, вы можете использовать команду UNIX «вставить» 🙂
2. да, я это сделал, но это проблема, когда отсутствуют строки. Я также пытался использовать команду join, но мне пришлось сортировать и записывать файлы для каждого отсортированного файла. Я просто нашел это действительно уродливым. и это не касалось случая, когда некоторые значения отсутствуют
3. предлагаемое решение с использованием хэша, вероятно, является самым простым решением!
Ответ №1:
Я не знаю другого способа, но это создаст хэш со всем в нем:
files = ['file1.txt', 'file2.txt', 'file3.txt']
result = Hash.new
files.each_with_index do |file, i|
File.foreach(file) do |line|
key, value = /(w)s (.*)/.match(line).captures
result[key] = Array.new(files.size) unless result.has_key?(key)
result[key][i] = value
end
end
Хэш result
выглядит следующим образом:
{"a" => ["3", "10.3 -2", nil],
"b" => ["4", "4.7 -1", "T"],
"c" => ["8", "8.9 -2", "F"],
"d" => ["22", nil, "T"],
"e" => ["4", "22.1 -1", nil],
"f" => [nil, nil, "F"],
"g" => [nil, nil, "T"]}
Ответ №2:
Вы могли бы сделать что-то вроде этого:
require 'csv'
def load(file)
CSV.open(file, :col_sep => "t").
each_with_object({ }) { |r, h| h[r.shift] = r }
end
# Load it all into hashes with a convenient format.
# The PK will be the key, the rest of the row will be the value as an array.
file1 = load('file1.txt')
file2 = load('file2.txt')
file3 = load('file3.txt')
# Figure out the rows in the final table
rows = (file1.keys | file2.keys | file3.keys).each_with_object({}) { |k,h| h[k] = [] }
# Use the block form of Hash#merge to join.
cols = [file1, file2, file3].inject([]) {|a, f| a.push(a.last.to_i f.first.last.length)}
joined = rows.merge(file1).
merge(file2) { |k, o, n| (o [nil] * (cols[0] - o.length)) n }.
merge(file3) { |k, o, n| (o [nil] * (cols[1] - o.length)) n }
# Patch any missing values in the last column.
joined.each { |k, v| v.concat([nil] * (cols[2] - v.length)) }
Результатом является хэш, подобный этому:
{"a"=>["3", "10.3", "-2", nil],
"b"=>["4", "4.7", "-1", "T"],
"c"=>["8", "8.9", "-2", "F"],
"d"=>["22", nil, nil, "T"],
"e"=>["4", "22.1", "-1", nil],
"f"=>[nil, nil, nil, "F"],
"g"=>[nil, nil, nil, "T"]}
При желании вы можете легко преобразовать это в массив массивов. Обобщение на несколько файлов также должно быть довольно простым. Конечно, есть и другие способы реализации различных шагов, но я оставлю эти уточнения в качестве упражнения.
Если файлы были большими, вам было бы лучше поместить их в базу данных SQLite и выполнить объединение в SQL.
Ответ №3:
Вы можете попробовать использовать Sequel для загрузки файлов через ODBC или ADO, если вы используете Windoze, затем выполните соединение там и выведите массив dataset в текстовый файл в том же формате.
Или импортируйте файлы в sqlite (см. Основные инструкции здесь, возможно, вам потребуется преобразовать табуляции в другой разделитель, не уверен), затем сделайте то же самое в Sequel, используя вместо этого адаптер sqlite.