Ruby сравнивает строки внутри хэша

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