Хотя ваш файл журнала не CSV, я нахожу библиотеку csv полезной во многих случаях, не связанных с CSV. Вы можете использовать его для анализа файла журнала, пропустив пустые строки и любую строку, начинающуюся с --- , === или ONUS . В качестве разделителя столбцов используется пробел:
csv = CSV.read("./example.log", skip_lines: /\A(---|===|ONUS)/,
skip_blanks: true, col_sep: " ")
Тогда в некоторых строках разбирается только 1 элемент в массиве, это строки заголовка. Таким образом, мы можем разбить массив csv
на группы, основываясь на том, что у нас есть только 1 элемент, и создать хеш из результата:
output_hash = csv.slice_before { |row| row.length == 1 }.
each_with_object({}) do |((name), *rows), hash|
hash[name] = rows.to_h
end
Теперь немного сложно сказать, хотите ли вы, чтобы вывод хеша отображался как текст, который вы показали, или вы просто хотели получить хеш. Если вы хотите вывод текста, сначала нам нужно увидеть, сколько места нужно отобразить в каждом столбце:
name_length = output_hash.keys.max_by(&:length).length
list_length = output_hash.values.flat_map(&:keys).max_by(&:length).length
detail_length = output_hash.values.flat_map(&:values).max_by(&:length).length
format = "%-#{name_length}s %-#{list_length}s %-#{detail_length}s"
и затем мы можем вывести строку заголовка и все значения в output_hash
, но только если они имеют какие-либо значения:
puts("#{format}\n\n" % ["NAME", "LIST", "DETAILS"])
output_hash.reject { |name, values| values.empty? }.each do |name, values|
list, detail = values.first
puts(format % [name, list, detail])
values.drop(1).each do |list, detail|
puts(format % ['', list, detail])
end
puts
end
и результат:
NAME LIST DETAILS
USOLA51 ICC_ONUS .035400391
PA_ONUS .039800391
PE_ONUS .000610352
USOLA10 PAL 52.7266846
CFG_ONUS 15.9489746
USOLA55 PA_ONUS 47.4707031
PAL 3.956604
ICC_ONUS .020385742
PE_ONUS .000610352
Немного сложно объяснить (для меня), что делает slice_before
. Но он принимает массив (или другой перечисляемый) и создает группы или чанки своего элемента, где первый элемент соответствует параметру или блок возвращает true. Например, если у нас был меньший массив:
array = ["slice here", 1, 2, "slice here", 3, 4]
array.slice_before { |el| el == "slice here" }.entries
# => [["slice here", 1, 2], ["slice here", 3, 4]]
Мы сказали slice_before
, что мы хотим, чтобы каждая группа начиналась с элемента, равного «срез здесь», поэтому мы возвращаем 2 группы, первый элемент в каждой «срез здесь», а остальные элементы - все элементы в массиве, пока в следующий раз не увидит «здесь».
Итак, мы можем взять этот результат и вызвать для него each_with_object
, передавая пустой хеш для начала. При each_with_object
первый параметр будет элементом массива (из каждого), а второй будет объектом, который вы передали. Что происходит, когда параметры блока выглядят как |((name), *rows), hash|
, так это то, что первый параметр (элемент массива) деконструируется в первый элемент массива и остальные элементы:
# the array here is what gets passed to `each_with_object` for the first iteration as the first parameter
name, *rows = [["USOLA51"], ["ICC_ONUS", ".035400391"], ["PA_ONUS", ".039800391"], ["PE_ONUS", ".000610352"]]
name # => ["USOLA51"]
rows # => [["ICC_ONUS", ".035400391"], ["PA_ONUS", ".039800391"], ["PE_ONUS", ".000610352"]]
Итак, мы снова деконструируем этот первый элемент, просто чтобы у нас его не было в массиве:
name, * = name # the `, *` isn't needed in the block parameters, but is needed when you run these examples in irb
name # => "USOLA51"
Для max_by(&:length).length
все, что мы делаем, это находим самый длинный элемент в массиве (возвращаемый либо keys
, либо values
) и получая его длину:
output_hash = {"USOLA51"=>{"ICC_ONUS"=>".035400391", "PA_ONUS"=>".039800391", "PE_ONUS"=>".000610352"}, "USOLA10"=>{"PAL"=>"52.7266846", "CFG_ONUS"=>"15.9489746"}, "USOLA55"=>{"PA_ONUS"=>"47.4707031", "PAL"=>"3.956604", "ICC_ONUS"=>".020385742", "PE_ONUS"=>".000610352"}, "USOLA56"=>{}}
output_hash.values.flat_map(&:keys)
# => ["ICC_ONUS", "PA_ONUS", "PE_ONUS", "PAL", "CFG_ONUS", "PA_ONUS", "PAL", "ICC_ONUS", "PE_ONUS"]
output_hash.values.map(&:length) # => [8, 7, 7, 3, 8, 7, 3, 8, 7]
output_hash.values.flat_map(&:keys).max_by(&:length) # => "ICC_ONUS"
output_hash.values.flat_map(&:keys).max_by(&:length).length # => 8