2014-11-11 6 views
2

Предположим, что у меня есть:Хэши в хэши, которые производят по умолчанию массив

a = Hash.new 
a['google.com'][:traffic] << 50 
a['google.com'][:traffic] << 20 
a['google.com'][:popular] << 1 
a['google.com'][:popular] << 2 
3a['yahoo.com'][:anythinghere] << 3 

нужно производить что-то вроде этого:

a = { 'google.com' => {traffic: [50,20], popular: [1,2]}, 'yahoo.com' => { anythinghere: [3,4] } } 

До сих пор я пытался что-то такого рода в надежде, что она будет произвести этот результат:

a= Hash.new(Hash.new(Array.new)) 

Например, a['google.com'] будет производить новый хэш то время как a['google.com'][:anythinghere] создаст новый массив. Однако, когда я пытаюсь выполнить указанные выше вставки, a пуст? Не знаю, что происходит, я почти уверен, что у меня нет чего-то фундаментального. Посмотрите:

a = stats = Hash.new(Hash.new(Array.new)) 
a['google.com'][:traffic] << 5 
a['google.com'][:traffic] << 6 
p a['google.com'][:traffic] #=> [5,6] 
p a['google.com'] #=> empty hash??? 
p a #=> empty hash??? 

ответ

4

Причина, по которой вы получаете это неожиданное поведение, заключается в том, что когда вы передаете значение по умолчанию в значение Hash в качестве аргумента new, то один объект - значение по умолчанию для всех ключей. Например:

s = "but" 
a = Hash.new(s) 
a['x'].concat 't' 
puts a['y'] 
# butt 
puts s 
# butt 

Это имеет смысл, так как рубин передает объекты, используя указатели, так что всякий раз, когда вы получите значение по умолчанию, это действительно указатель на исходный объект, который вы передали.

Чтобы обойти это, вы можете установить значение по умолчанию в блоке. Таким образом Хэш должен переоценивать блока каждый раз, когда он нуждается в значение по умолчанию:

a = Hash.new { "but" } 
a['x'].concat 't' 
puts a['x'] 
# but 
puts a.size 
# 0 

Следующая Гоча является то, что, когда Руби получает значение по умолчанию, не назначать его на любые клавиши. Вот почему размер хэша по-прежнему равен 0 после доступа к ключу в предыдущем примере. Эта проблема может быть также работал примерно с рубин поставляет сам хэш и отсутствующий ключ к блоку значения по умолчанию, так что мы можем выполнить задание там:

a = Hash.new { |hash, key| hash[key] = "but" } 
a['x'].concat 't' 
puts a['x'] 
# butt 
puts a['y'] 
# but 
puts a.size 
# 2 
+0

Мне нравятся ответы, после которых вы можете пойти и получить решение самостоятельно. Благодарю. – daremkd

1

Вы можете использовать эту строку: Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } }

a = Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } } 
a['google.com'][:traffic] << 50 
a['google.com'][:traffic] << 20 
a['google.com'][:popular] << 1 
a['google.com'][:popular] << 2 
a['yahoo.com'][:anythinghere] << 3 
a 
# => {"google.com"=>{:traffic=>[50, 20], :popular=>[1, 2]}, "yahoo.com"=>{:anythinghere=>[3]}} 

я найти блок, подаваемое Hash.new немного грязный, поэтому он может быть очищен так:

def Hash.default_set(&block) 
    Hash.new { |h, k| h[k] = block.call } 
end 
a = Hash.default_set { Hash.default_set { [] } } 
# ... 
0

Другой способ:

код

def hashify(arr) 
    arr.each_with_object({}) do |e,h| 
    *hash_keys, array_key, value = e 
    g = hash_keys.reduce(h) { |g,k| g[k] ||= {} } 
    (g[array_key] ||= []) << value 
    end 
end 

Примеры

a = [['google.com', :traffic, 50], 
    ['google.com', :traffic, 20], 
    ['google.com', :popular, 1], 
    ['google.com', :popular, 2], 
    ['yahoo.com', :anythinghere, 3], 
    ['yahoo.com', :anythinghere, 4]] 

hashify(a) 
    #=> {"google.com"=>{:traffic=>[50, 20], 
    # :popular=>[1, 2]}, 
    # "yahoo.com"=>{:anythinghere=>[3, 4]}} 

a = [['google.com', :traffic, 50], 
    ['google.com', :traffic1, :cat, :dog, 20], 
    ['google.com', :traffic1, :cat, :dog, 30], 
    ['google.com', :popular, 1], 
    ['google.com', :popular, 2], 
    ['yahoo.com', :anythinghere, 3], 
    ['yahoo.com', :anythinghere, 4]] 

hashify(a) 
    #=> {"google.com"=>{:traffic=>[50], :traffic1=>{:cat=>{:dog=>[20, 30]}}, 
    # :popular=>[1, 2]}, 
    # "yahoo.com"=>{:anythinghere=>[3, 4]}} 
Смежные вопросы