【问题标题】:Appending to an array value in a hash附加到哈希中的数组值
【发布时间】:2016-02-08 20:50:15
【问题描述】:

我正在解析多个网站并尝试构建一个类似于以下内容的哈希:

"word" => [[01.html, 2], [02.html, 7], [03.html, 4]]

其中 word 是索引中的给定单词,每个子列表中的第一个值是找到它的文件,第二个值是该给定文件中的出现次数。

我遇到了一个问题,它不是在值列表中附加["02.html", 7],而是为“word”创建一个全新的条目并将["02.html", 7] 放在哈希的末尾。这基本上会为我的所有网站提供单独的索引,而不是给我一个主索引。

这是我的代码:

for token in tokens
   if !invindex.include?(token)
     invindex[token] = [[doc_name, 1]] #adds the word to the hash with the doc name and  occurrence of 1
   else
     for list in invindex[token]
       if list[0] == doc_name
         list[1] += 1 #adds one to the occurrence with the same doc_name
       else
         invindex[token].insert([doc_name, 1]) #this SHOULD append the doc name and initial occurrence inside the word's value list since the word is already in the hash
       end
     end
   end
 end
end

希望它是简单的,当我在纸上追踪它时,我只是错过了一些东西。

【问题讨论】:

    标签: arrays ruby indexing hash


    【解决方案1】:

    我遇到了一个问题,而不是附加 ["02.html", 7] 在值列表中,它为“word”创建了一个全新的条目,并且 将 ["02.html", 7] 放在哈希的末尾。

    我没看到:

    invindex = {
      word1: [ 
        ['01.html', 2],
      ]
    }
    
    tokens = %i[
      word1
      word2
      word3
    ]
    
    doc_name = '02.html'
    
    tokens.each do |token|
      if !invindex.include?(token)
        invindex[token] = [[doc_name, 1]] #adds the word to the hash with the doc name and  occurrence of 1
      else
        invindex[token].each do |list|
          if list[0] == doc_name
            list[1] += 1 #adds one to the occurrence with the same doc_name
          else
            invindex[token].insert([doc_name, 1]) #this SHOULD append the doc name and initial occurrence inside the word's value list since the word is already in the hash
          end
        end
      end
    
    end
    
    p invindex
    
    --output:--
    {:word1=>[["01.html", 2]], :word2=>[["02.html", 1]], :word3=>[["02.html", 1]]}
    

    invindex[token].insert([doc_name, 1]) #this SHOULD append the doc name

    没有:

    invindex = {
      word: [ 
        ['01.html', 2],
      ]
    }
    
    token = :word
    doc_name = '02.html'
    
    invindex[token].insert([doc_name, 7])
    p invindex
    invindex[token].insert(-1, ["02.html", 7])
    p invindex
    
    --output:--
    {:word=>[["01.html", 2]]}
    {:word=>[["01.html", 2], ["02.html", 7]]}
    

    Array#insert() 要求您将索引指定为第一个参数。通常,当您想在末尾附加一些内容时,您使用<<:

    invindex = {
      word: [ 
        ['01.html', 2],
      ]
    }
    
    token = :word
    doc_name = '02.html'
    
    invindex[token] << [doc_name, 7]
    p invindex
    
    --output:--
    {:word=>[["01.html", 2], ["02.html", 7]]}  
    

    for token in tokens

    Ruby 爱好者不使用for-in 循环,因为for-in 循环调用each(),所以Ruby 爱好者直接调用each():

    tokens.each do |token|
      ...
    end
    

    最后,indenting in ruby 是 2 个空格——不是 3 个空格,不是 1 个空格,也不是 4 个空格。是 2 个空格。

    将所有这些应用到您的代码中:

    invindex = {
      word1: [ 
        ['01.html', 2],
      ]
    }
    
    tokens = %i[
      word1
      word2
      word3
    ]
    
    doc_name = '01.html'
    
    tokens.each do |token|
      if !invindex.include?(token)
        invindex[token] = [[doc_name, 1]] #adds the word to the hash with the doc name and  occurrence of 1
      else
        invindex[token].each do |list|
          if list[0] == doc_name
            list[1] += 1 #adds one to the occurrence with the same doc_name
          else
            invindex[token] << [doc_name, 1] #this SHOULD append the doc name and initial occurrence inside the word's value list since the word is already in the hash
          end
        end
      end
    
    end
    
    p invindex
    
    --output:--
    {:word1=>[["01.html", 3]], :word2=>[["01.html", 1]], :word3=>[["01.html", 1]]}
    

    但是,仍然存在一个问题,这是因为您正在更改一个正在逐步执行的数组——这是计算机编程中的一大禁忌:

       invindex[token].each do |list|
          if list[0] == doc_name
            list[1] += 1 #adds one to the occurrence with the same doc_name
          else
            invindex[token] << [doc_name, 1]  #***PROBLEM***
    

    看看会发生什么:

    invindex = {
      word1: [ 
        ['01.html', 2],
      ]
    }
    
    tokens = %i[
      word1
      word2
      word3
    ]
    
    %w[ 01.html 02.html].each do |doc_name|
    
      tokens.each do |token|
        if !invindex.include?(token)
          invindex[token] = [[doc_name, 1]] #adds the word to the hash with the doc name and  occurrence of 1
        else
          invindex[token].each do |list|
            if list[0] == doc_name
              list[1] += 1 #adds one to the occurrence with the same doc_name
            else
              invindex[token] << [doc_name, 1] #this SHOULD append the doc name and initial occurrence inside the word's value list since the word is already in the hash
            end
          end
        end
    
      end
    end
    
    p invindex
    
    --output:--
    {:word1=>[["01.html", 3], ["02.html", 2]], :word2=>[["01.html", 1], ["02.html", 2]], :word3=>[["01.html", 1], ["02.html", 2]]}
    

    问题 1: 您不想在每次检查的子数组不包含doc_name 时插入[doc_name, 1]——您只想在之后插入[doc_name, 1]已检查所有子阵列,但未找到 doc_name。如果您使用起始哈希运行上面的示例:

    invindex = {
      word1: [ 
        ['01.html', 2],
        ['02.html', 7],
      ]
    }
    

    ...你会看到输出更糟。

    问题 2: 在遍历数组时将[doc_name, 1] 附加到数组意味着当循环到达数组末尾时,[doc-name, 1] 也将被检查——然后您的循环会将其计数增加到 2。规则是:不要更改您正在单步执行的数组,因为会发生不好的事情。

    【讨论】:

    • 感谢您的帮助。我接受了您的建议,并在迭代时避免编辑数组。我最终创建了一个“包含”变量,如果其中一个子数组具有 doc_name,则该变量将从 False 更改为 True。在迭代结束时,如果 contains 仍然是 False,那么我将在最后添加新的子列表。我是 Ruby 和一般编程的新手,我有点被抛到脑后,很明显我有很多东西要学,所以谢谢!
    【解决方案2】:

    你真的需要一个包含数组数组的哈希吗?

    这可以用嵌套散列更好地描述

    invindex = {
      "word" => { '01.html' => 2, '02.html' => 7, '03.html' => 4 },
      "other" => { '01.html' => 1, '02.html' => 17, '04.html' => 4 }
    }
    

    可以通过使用类似的哈希工厂轻松填充

    invindex = Hash.new { |h,k| h[k] = Hash.new {|hh,kk| hh[kk] = 0} }
    tokens.each do |token|
      invindex[token][doc_name] += 1
    end
    

    现在,如果您绝对需要您提到的格式,您可以通过简单的迭代从描述的invindex 获得它

    result = {}
    invindex.each {|k,v| result[k] = v.to_a }
    

    【讨论】:

      【解决方案3】:

      假设:

      arr = %w| 01.html 02.html 03.html 02.html 03.html 03.html |
        #=> ["01.html", "02.html", "03.html", "02.html", "03.html", "03.html"] 
      

      是索引中给定单词的文件数组。然后通过构造计数哈希来给出哈希中那个词的值:

      h = arr.each_with_object(Hash.new(0)) { |s,h| h[s] += 1 }
        #=> {"01.html"=>1, "02.html"=>2, "03.html"=>3}
      

      然后将其转换为数组:

      h.to_a
        #=> [["01.html", 1], ["02.html", 2], ["03.html", 3]]
      

      所以你可以写:

      arr.each_with_object(Hash.new(0)) { |s,h| h[s] += 1 }.to_a
      

      Hash::new 的默认值为零。这意味着如果正在构造的哈希 h 没有键 s,h[s] 将返回零。在这种情况下:

      h[s] += 1
        #=> h[s] = h[s] + 1
        #        = 0 + 1 = 1
      

      并且当arr 中的s 的相同值传递给块时:

      h[s] += 1
        #=> h[s] = h[s] + 1
        #        = 1 + 1 = 2
      

      您可以考虑将索引中每个单词的值设为哈希h是否更好。

      【讨论】:

        猜你喜欢
        • 2018-01-28
        • 1970-01-01
        • 1970-01-01
        • 2014-01-10
        • 1970-01-01
        • 2013-06-13
        • 2013-02-13
        • 2021-05-08
        • 2013-12-04
        相关资源
        最近更新 更多