2014-11-25 7 views
-1

У меня есть следующий хэш в Ruby:Hash и частота значения

{ 
    0 => { 
     :method=> "POST", 
     :path=> "/api/customer/191023", 
     :host=> "host.8", 
     :duration=> "1221" 
    }, 

    1 => { 
     :method=> "GET", 
     :path=> "/api/customer/191023", 
     :host=> "host.8", 
     :duration=> "99" 
    }, 

    2 => { 
     :method=> "POST", 
     :path=> "/api/customer/191023", 
     :host=> "host.10", 
     :duration=> "142" 
    }, 

    3 => { 
     :method=> "POST", 
     :path=> "/api/customer/191023", 
     :host=> "host.8", 
     :duration=> "243" 
    } 

    4 => { 
     :method=> "POST", 
     :path=> "/api/customer/191023", 
     :host=> "host.10", 
     :duration=> "132" 
    } 
} 

Я хотел бы сделать простой поиск в этих хэшей, чтобы найти host с самой высокой частотой. Например, в предыдущем примере я должен получить host.8.

Спасибо за вашу помощь,

М.

+0

Возможный дубликат [Поиск по хэшу, содержащему хэши] (http://stackoverflow.com/questions/27126400/search-within-a-hash-containing-hashes) –

+0

Возможно, вы захотите сделать редактирование, чтобы исправить проблема форматирования строки, о которой я упоминал в своем ответе. Я ожидаю, что это приведет к сокращению голосов и голосам. Я удалю этот комментарий после того, как вы его увидели, - ответ не нужен. –

+0

Готово, я обновил свой вопрос. – Mich

ответ

1

Вот один из способов сделать это.

Код

def max_host(hash) 
    hash.each_with_object(Hash.new(0)) { |(_,v),h| h[v[:host]] += 1 } 
     .max_by { |_,v| v } 
     .first 
end 

Пример

Давайте рассмотрим упрощенный пример ниже. Обратите внимание, что я изменил, например, :host = \"host.10\" на :host = "host.10", поскольку первый не является правильным синтаксисом. Вы можете написать строку как '\"host.10\" (=> "\\\"host.10\\\""), но я предполагаю, что вы просто хотите "host.10". Код тот же для обоих.

hash = { 
    0 => { 
     :method=>"POST", 
     :host =>"host.8" 
    }, 

    1 => { 
     :method=>"GET", 
     :host =>"host.10" 
    }, 

    2 => { 
     :method=>"POST", 
     :host =>"host.10" 
    } 
} 

max_host(hash) 
    #=> "host.10" 

Объяснение

Для примера hash выше,

enum = hash.each_with_object(Hash.new(0)) 
    #=> #<Enumerator: { 
    #  0=>{:method=>"POST", :host=>"host.8"}, 
    #  1=>{:method=>"GET", :host=>"host.10"}, 
    #  2=>{:method=>"POST", :host=>"host.10"}}:each_with_object({})> 

Перечислитель будет вызывать метод Hash#each пройти каждый элемент счетчику в блок. Мы можем видеть, что эти элементы путем преобразования нумератора в массив:

enum.to_a 
    #=> [[[0, {:method=>"POST", :host=>"host.8"}], {}], 
    # [[1, {:method=>"GET", :host=>"host.10"}], {}], 
    # [[2, {:method=>"POST", :host=>"host.10"}], {}]] 

Пустой хеш показан в первом элементе является начальным значением хэша созданного

Hash.new(0) 

Это создает хэш h со значением по умолчанию, равным нулю. Таким образом, если у h нет ключа k, h[k] вернет значение по умолчанию (0), но (важно!) Это не изменит хэш.

Первое значение, переданное в блоке

[[0, {:method=>"POST", :host=>"host.8"}], {}] 

Это разлагают (или «многозначной») на отдельные объекты, которые назначены на три блока переменных:

k => 0 
v => {:method=>"POST", :host=>"host.8"} 
h => Hash.new(0) 

Затем мы выполняем :

h[v[:host]] += 1 

который

h["host.8"] += 1 

который является сокращением для

h["host.8"] = h["host.8"] + 1 

[За исключением: вы, возможно, заметили, что в коде показывает блочные переменные |(_,v),h|, в то время как выше я имею в виду их выше как |(k,v),h|. Я мог бы использовать последнее, но поскольку k не является ссылкой в ​​блоке, я решил заменить его «заполнителем» _. Это гарантирует k не будет ссылаться, а также говорит, что любые читатели я не использую то, что бы быть первой переменной блок.]

Как h не имеет ключа "host.8", h["host.8"] справа от = возвращает значение по умолчанию значение:

h["host.8"] = 0 + 1 
    #=> 1 

так что теперь

h #=> {"host.8"=>1} 

Второй элемент передается в блок является

[[1, {:method=>"GET", :host=>"host.10"}], {"host.8"=>1}] 

так блочные переменные становятся:

v => {:method=>"GET", :host=>"host.10"} 
h => {"host.8"=>1} 

Обратите внимание, что хэш h был обновлен. Мы выполняем

h[v[:host]] += 1 
    #=> h["host.10"] += 1 
    #=> h["host.10"] = h["host.10"] + 1 
    #=> h["host.10"] = 0 + 1 
    #=> 1 

так что теперь

h #=> {"host.8"=>1, "host.10"=>1} 

Наконец, блок-переменные присваиваются значения

v = {:method=>"POST", :host=>"host.10"} 
h => {"host.8"=>1, "host.10"=>1} 

так

h[v[:host]] += 1 
    #=> h["host.10"] += 1 
    #=> h["host.10"] = h["host.10"] + 1 
    #=> h["host.10"] = 1 + 1 
    #=> 2 

h #=> {"host.8"=>1, "host.10"=>2} 

и значение h то возвращается б y метод.

+0

Огромное спасибо за этот удивительный ответ. :) – Mich

2

Чтобы найти значение хоста с высокой частотой делать:

hs = hash.values.group_by { |h| h[:host] =~ /host\.(\d+)/ && $1.to_i || 0 }.to_a 
hs.reduce([-1,0]) { |sum,v| v[1].size > sum[1] && [ v[0], v[1].size ] || sum }.first 

Описание:[-1,0] значение по умолчанию для набора для #reduce method, где -1 - это номер (как в host.number), а 0 - это счетчик числа. Таким образом, когда сокращение встречает число с размером больше, чем пройденное sum, оно заменяется новым значением на следующей итерации.

+0

Спасибо, но что такое переменная «v»? Это опечатка? – Mich

+0

@Mich да, извините –

+0

@Mich обновлено снова –

Смежные вопросы