#ruby
#ruby
Вопрос:
Я подготовил метод, который анализирует файл журнала и возвращает хэш в результате:
parse
def parse
file_exist?(file_path)
@page_views = Hash.new { |k, v| k[v] = [] }
File.open(file_path).each do |line|
page, ip = line.split
@page_views[page] << ip
end
@page_views
end
result
=> {"/help/1"=>
["126.318.035.038",
"929.398.951.889",
"543.910.244.929",
"929.398.951.889",
"929.398.951.889",],
"/about"=>
["929.398.951.889",
"929.398.951.889",
"543.910.244.929",]
Как отсортировать такие данные для подсчета IP-адресов и отображения, как показано ниже:
=> {"/help/1"=>
["126.318.035.038" => 1,
"929.398.951.889" => 3,
"543.910.244.929" => 1]
"/about"=>
["929.398.951.889" => 2,
"543.910.244.929" => 1]
Комментарии:
1. Погуглите «гистограмму».
2. Вызовите
tally
IP-массивы.
Ответ №1:
Использовать tally
.
@page_views.update(@page_views) {|_, v| v.tally}
Комментарии:
1. Я предлагаю
@page_views.transform_values { |v| v.tally }
, илиtransform_values!
если (как в вашем ответе) хэш должен быть изменен на месте.
Ответ №2:
Вы можете попробовать с этим
hash = {"/help/1"=>
["126.318.035.038",
"929.398.951.889",
"543.910.244.929",
"929.398.951.889",
"929.398.951.889",],
"/about"=>
["929.398.951.889",
"929.398.951.889",
"543.910.244.929",]
}
массив хэшей ip-адреса с количеством
hash.transform_values {|v| v.uniq.collect{ |e| [[e, v.count(e)]].to_h } }
=> {
"/help/1"=>[{"126.318.035.038"=>1},
{"929.398.951.889"=>3},
{"543.910.244.929"=>1}],
"/about"=>[{"929.398.951.889"=>2},
{"543.910.244.929"=>1}]
}
хэш IP-адресов с подсчетом
hash.transform_values {|v| v.uniq.collect{ |e| [e, v.count(e)] }.to_h }
=> {"/help/1"=>{
"126.318.035.038"=>1,
"929.398.951.889"=>3,
"543.910.244.929"=>1},
"/about"=>{
"929.398.951.889"=>2,
"543.910.244.929"=>1}
}
используйте приведенный ниже код, если у вас более новый ruby, я тестировал это на 2.7
hash.transform_values(amp;:tally)
{"/help/1"=>{
"126.318.035.038"=>1,
"929.398.951.889"=>3,
"543.910.244.929"=>1},
"/about"=>{
"929.398.951.889"=>2,
"543.910.244.929"=>1}
}
Ответ №3:
Вы можете использовать IO::foreach с хэшем, к которому привязана процедура по умолчанию, содержащая вторую процедуру по умолчанию.
Код
def doit(file_name)
IO.foreach(file_name).
with_object(Hash.new { |h,k| h[k] = Hash.new(0) }) do |line,h|
key, value = line.chomp.split
h[key][value] = 1
end
end
Пример
Сначала создайте файл для иллюстрации.
file_name = 't'
str =<<~END
cat 1
cat 1
cat 1
cat 2
cat 2
dog 1
dog 2
dog 2
dog 3
dog 3
pig 1
pig 1
pig 1
pig 2
END
Рандомизируйте строки str
:
str = str.lines.shuffle.join
#=> "cat 1ndog 1npig 1ndog 3ncat 1npig 1ndog 2ndog 2npig 1ncat 2ndog 3ncat 1ncat 2npig 2n"
puts str
cat 1
dog 1
pig 1
dog 3
cat 1
pig 1
dog 2
dog 2
pig 1
cat 2
dog 3
cat 1
cat 2
pig 2
Создайте файл, содержащий изображение str
:
File.write(file_name, str)
#=> 84
Выполните метод:
doit(file_name)
#=> {"cat"=>{"1"=>3, "2"=>2},
# "dog"=>{"1"=>1, "3"=>2, "2"=>2},
# "pig"=>{"1"=>3, "2"=>1}}
Сравните это с исходным значением str
выше.
Примечания
См. IO::foreach и форму Hash::new, в которой используется процедура по умолчанию.
Обратите внимание, что, поскольку
IO.foreach(file_name).
with_object(Hash.new { |h,k| h[k] = Hash.new(0) })
#=> #<Enumerator: #<Enumerator: IO:foreach("t")>:with_object({})>
является перечислителем, промежуточный хэш (с домашними животными в качестве ключей и массивами строк, представляющих неотрицательные целые числа в качестве значений) не создается, что является преимуществом по сравнению с методами, которые это делают. Такой промежуточный хэш требуется, например, для использования Enumerable#tally (введено в Ruby версии 2.7).