【问题标题】:How to parse data from a file into a Hash如何将文件中的数据解析为哈希
【发布时间】:2019-05-22 09:52:24
【问题描述】:

我正在尝试解析文件中的一些数据并将其存储到哈希映射中,而不是使用正则表达式而是字符串比较,并且我遇到了一些我尝试修复但没有解决问题的错误。

文件的结构如下:

"key" + "double colon" + "value"

在每一行。这种结构沿文件重复,每个数据都有一个 ID 键,几乎所有数据都至少有一个“is_a”键,还可能有“is_obsolete”和“replaced_by”键。

我试图像这样解析它:

def get_hpo_data(hpofile="hp.obo")
    hpo_data = Hash.new() #Hash map where i want to store all IDs
    File.readlines(hpofile).each do |line|
        if line.start_with? "id:" #if line is an ID
            hpo_id = line[4..13]  #Store ID value
            hpo_data[hpo_id] = Hash.new() #Setting up hash map for that ID
            hpo_data[hpo_id]["parents"] = Array.new()

        elsif line.start_with? "is_obsolete:" #If the ID is obsolete
            hpo_data[hpo_id]["is_obsolete"] = true #store value in the hash

        elsif line.start_with? "replaced_by:" #If the ID is obsolete
            hpo_data[hpo_id]["replaced_by"] = line[13..22]
            #Store the ID term it was replaced by

        elsif line.start_with? "is_a:" #If the ID has a parent ID
            hpo_data[hpo_id]["parents"].push(line[6..15])
            #Store the parent(s) in the array initialized before
        end
    end
    return hpo_data
end

我期望创建的结构是一个全局哈希,其中每个 ID 也是一个带有不同数据的哈希(一个字符串数据、一个布尔值和一个可变长度的数组,具体取决于该 ID 的父 ID 的数量)术语,但我收到以下错误:

table_combination.rb:224:in `block in get_hpo_data': undefined method `[]=' for nil:NilClass (NoMethodError)

这次错误指向replaced_byelsif 语句,但我也可以通过任何其他elsif 语句得到它,因此代码无法解析“is_obsolete”、“replaced_by”和“is_a” “ 特性。如果我尝试删除这些语句,代码会成功创建全局散列,并将每个 ID 项作为散列。

我也尝试为每个哈希值提供默认值,但这并不能解决问题。我什至遇到了一个以前从未见过的新错误:

table_combination.rb:233:in '[]': no implicit conversion of String into Integer (TypeError)

在这一行:

hpo_data[hpo_id]["parents"].push(line[6..15])

以下是两个术语的文件外观示例,显示了我要处理的不同键:

[Term]
id: HP:0002578
name: Gastroparesis
def: "Decreased strength of the muscle layer of stomach, which leads to a decreased ability to empty the contents of the stomach despite the absence of obstruction." [HPO:probinson]
subset: hposlim_core
synonym: "Delayed gastric emptying" EXACT layperson [ORCID:0000-0001-5208-3432]
xref: MSH:D018589
xref: SNOMEDCT_US:196753007
xref: SNOMEDCT_US:235675006
xref: UMLS:C0152020
is_a: HP:0002577 ! Abnormality of the stomach
is_a: HP:0011804 ! Abnormal muscle physiology

[Term]
id: HP:0002564
name: obsolete Malformation of the heart and great vessels
is_obsolete: true
replaced_by: HP:0030680

【问题讨论】:

  • 欢迎来到 SO!我们需要可运行并演示问题的示例代码。您要求我们编写一个测试工具来证明/反驳您的代码并对其进行调试,但这是您的工作。 How to Ask 将帮助您了解我们的需求。
  • @theTinMan 这个问题并不完美,但它仍然可以很好地显示输入是什么,问题是什么以及 OP 尝试了什么。

标签: ruby parsing


【解决方案1】:

您的代码中可能隐藏了更多错误,但一个问题确实是您的hpo_data 没有默认值。

如果 hpo_id 尚未初始化,则调用 hpo_data[hpo_id]["replaced_by"] = line[13..22] 会失败。

你可以这样定义hpo_data

hpo_data = Hash.new { |hash, key| hash[key] = {'parents' => [] } }

并删除

hpo_data = Hash.new() #Hash map where i want to store all IDs

        hpo_data[hpo_id] = Hash.new() #Setting up hash map for that ID 
        hpo_data[hpo_id]["parents"] = Array.new()

任何时候调用hpo_data[hpo_id],都会自动定义为{"parents"=>[]}

举个例子:

hpo_data = Hash.new { |hash, key| hash[key] = {'parents' => [] } }
# => {} 
hpo_data[1234]
# => {"parents"=>[]} 
hpo_data[1234]["parents"] << 6
# => [6] 
hpo_data
# => {1234=>{"parents"=>[6]}} 
hpo_data[42]["is_obsolete"] = true
# => true 
hpo_data
# => {1234=>{"parents"=>[6]}, 42=>{"parents"=>[], "is_obsolete"=>true}}

【讨论】:

  • 非常感谢您的帮助!!它像你说的那样工作。我还必须将变量 hpo_id 移出块的范围才能工作,我不知道为什么,但主要问题在于哈希。非常感谢您的回答,我真的陷入了代码的那部分。
  • @JesusJPG:是的,我没有注意到。如果您只将hpo_id 留在each do |line| 块内,它只会为相应的行定义,并且将为以下行定义nil,即使您需要它作为参考。如果它解决了您的问题,请随意投票/接受答案。
猜你喜欢
  • 1970-01-01
  • 2013-03-06
  • 2011-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-29
  • 2013-04-25
  • 2019-04-04
相关资源
最近更新 更多