如何在哈希中连接字符串值

时间:2014-04-03 20:35:59

标签: ruby hash string-concatenation

我正在打开一个包含与代码相关的条款的文件。文件中的一行显示如下:

Pacific Ocean; D01.330.322

我希望它们出现在哈希中。有些术语出现不止一次,我希望将其值放在由","连接的一个字符串中。我的代码是:

descriptor_code_hash = Hash.new
File.open('mtrees2014.bin').each do |file_line|
  file_line = file_line.chomp
  mesh_descriptor, tree_code = file_line.split(/\;/)
  descriptor_code_hash[mesh_descriptor] = tree_code
  if descriptor_code_hash.has_key? mesh_descriptor
    tree_code << "," << tree_code 
  else 
    descriptor_code_hash[mesh_descriptor] 
  end
end 

当一个术语有多个代码时,相同的代码会连接一次,并且不会识别该术语的其他唯一代码。此外,我不知道如何编写脚本来获取所有代码,例如,一些术语有六个代码。

2 个答案:

答案 0 :(得分:1)

你的代码并不遥远。我们来看看吧。

测试数据

以下是一些用于测试的数据:

oceans = ["Pacific; 1", "Atlantic; 2", "Indian; 3",
          "Pacific; 2", "Atlantic; 1", "Pacific; 3"]

我没有读取文件的行,而是通过从字符串数组中读取来简化操作。一旦代码工作,它就足够简单,可以将其更改为从文件中读取。

现在我们有了一些输入数据,我们可以显示我们想要的预期结果,

hash =
  { 'Pacific'  => ['1', '2', '3'],
    'Atlantic' => ['2', '1'],
    'Indian'   => ['1'] }

hash =
  { 'Pacific'  => "'1', '2', '3'",
    'Atlantic' => "'2', '1'",
    'Indian'   => "'1'" }

我们将使用第一个,因为它最容易处理,如果我们想要第二个表单,我们可以从第一个表单轻松计算它:

hash.keys.each { |k| hash[k] = hash[k].join(',') }  
  #=> ["Pacific", "Atlantic", "Indian"]

但是,等等,这不是返回的哈希值。不,这是hash.keys。我们想要的是hash的新值:

hash #=> {"Pacific"=>"1,2,3", "Atlantic"=>"2,1", "Indian"=>"1"}  

除此之外:在向SO发布问题时,将一些说明性输入数据与预期结果一起包含通常很有帮助。这往往澄清,并保存文字。尝试使用尽可能少的数据。

您的代码

这是你的代码,数组oceans代替了文件的读取:

descriptor_code_hash = Hash.new
oceans.each do |file_line|
  file_line = file_line.chomp
  mesh_descriptor, tree_code = file_line.split(/\;/)
  descriptor_code_hash[mesh_descriptor] = tree_code

  if descriptor_code_hash.has_key? mesh_descriptor
    tree_code << "," << tree_code 
  else 
    descriptor_code_hash[mesh_descriptor] 
  end
end

主要问题是:

descriptor_code_hash[mesh_descriptor] = tree_code

每次循环时,键descriptor_code_hash的{​​{1}}值会重置为mesh_descriptor的当前元素tree_code的值(表示一行的文件)。你需要删除这一行。

接下来,我们需要更改您的oceans语句,如下所示:

if/else/end

这为您提供以下内容:

if descriptor_code_hash.has_key? mesh_descriptor
  descriptor_code_hash[mesh_descriptor] << tree_code 
else 
  descriptor_code_hash[mesh_descriptor] = [tree_code]
end

当我们运行时,我们获得:

descriptor_code_hash = Hash.new
oceans.each do |file_line|
  file_line = file_line.chomp
  mesh_descriptor, tree_code = file_line.split(/\;/)
  if descriptor_code_hash.has_key? mesh_descriptor
    descriptor_code_hash[mesh_descriptor] << tree_code 
  else 
    descriptor_code_hash[mesh_descriptor] = [tree_code]
  end
end

如您所见,结果是正确的,除了存在轻微的格式问题。我们可以通过改变来解决这个问题:

descriptor_code_hash
  #=> {"Pacific"=>[" 1", " 2", " 3"], "Atlantic"=>[" 2", " 1"],
  #    "Indian"=>[" 3"]}

file_line.split(/\;/)

可以通过两种方式简化:

file_line.split(/\;/).map { |w| w.strip }

我们来试试吧。假设:

file_line.split(';').map(&:strip)

然后

file_line = "Pacific; 1\n"

这是期望的结果。请注意,我在字符串的末尾添加了换行符。那是为了告诉你file_line.split(';').map(&:strip) #=> ["Pacific", "1"] 删除它以及空格。这意味着您不需要上一行:

strip

file_line = file_line.chomp 也有效。)

您的代码现在简化为:

file_line.chomp.split(/\s*;\s*/)

<强>抛光

现在考虑一下你可以做些什么来使你的代码更像Ruby。首先,查看@BroiSatse给出的答案中使用的以下行(代替您的descriptor_code_hash = Hash.new oceans.each do |file_line| mesh_descriptor, tree_code = file_line.split(';').map(&:strip) if descriptor_code_hash.has_key? mesh_descriptor descriptor_code_hash[mesh_descriptor] << tree_code else descriptor_code_hash[mesh_descriptor] = [tree_code] end end 构造):

if/else/end

对于任何变量(descriptor_code_hash[mesh_descriptor] ||= []) << tree_code aa ||= []相同。如果尚未定义a = (a || []),则它将等于a,因此nil。如果已为(nil || []) => []分配了(非零)值a。换句话说,如果(a || []) => a没有键descriptor_code_hash(意为mesh_descriptor),descriptor_code_hash[mesh_descriptor] => nil将被分配descriptor_code_hash[mesh_descriptor];否则,它将被自己分配(即,它不会改变)。

[]
执行

descriptor_code_hash[mesh_descriptor] ||= [] 将等于数组,空或其他。 descriptor_code_hash[mesh_descriptor]然后将<< tree_code附加到哈希值(数组)。最后,我们可以使用tree_code而不是{},但这纯粹是一种风格选择。

您的代码现在看起来像这样:

Hash.new

现在让我们将其作为一种方法并进行一些更改:

descriptor_code_hash = {}
oceans.each do |file_line|
  mesh_descriptor, tree_code = file_line.split(';').map(&:strip)
  (descriptor_code_hash[mesh_descriptor] ||= []) << tree_code
end

我简化了一些变量名称,因为该方法的目的是通过其名称来描述的。仔细阅读Enumerable#each_with_object的文档(自1.9版开始提供)以了解它的使用方法。

您可能希望将文件名作为方法参数。

最后一件事:你可以改写如下:

def descriptor_code_hash(oceans)
  oceans.each_with_object({}) do |line, hash|
    mesh_descriptor, tree_code = line.split(';').map(&:strip)
    (hash[mesh_descriptor] ||= []) << tree_code
  end
end

descriptor_code_hash(oceans)
  #=> {"Pacific"=>["1", "2", "3"], "Atlantic"=>["2", "1"], "Indian"=>["3"]}

此处对象初始化为:

def descriptor_code_hash(oceans)
  oceans.each_with_object(Hash.new {|k,h| h[k] = {} }) do |line, hash|
    mesh_descriptor, tree_code = line.split(';').map(&:strip)
    hash[mesh_descriptor] << tree_code
  end
end

使得默认值(对于将新键添加到哈希时)为空哈希。这就是为什么第三到最后一行可以简化如图所示。

答案 1 :(得分:0)

尝试:

descriptor_code_hash = Hash.new
File.open('mtrees2014.bin').each do |file_line|
  file_line = file_line.chomp
  mesh_descriptor, tree_code = file_line.split(/\;/)
  (descriptor_code_hash[mesh_descriptor] ||= []) << tree_code
end

这种方式descriptor_code_has[key]是一个数组,其中包含来自文件的给定键的所有代码。