Объединение таблиц из нескольких файлов

#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.