Ruby坏数组到Hash

时间:2014-07-09 16:20:07

标签: ruby arrays hash

我有一个看起来像这样的丑陋数组

["advert 0", "[1404915231, 1404920520]", "advert 4", "[1404915231]", "advert 5", "[1404915231]", "advert 6", "[1404915231]", "advert 7", "[1404915231]", "advert 8", "[1404915231]", "advert 9", "[1404915231]"]

我试图让它看起来像这样,但我没有成功。

{advert1: [1404915231, 1404920520], advert4: [1404915231]}

由于

3 个答案:

答案 0 :(得分:3)

这是一种方式(如果它可以帮助你): -

require 'yaml'

arr = [
       "advert 0", "[1404915231, 1404920520]",
       "advert 4", "[1404915231]", "advert 5",
       "[1404915231]", "advert 6", "[1404915231]",
       "advert 7", "[1404915231]", "advert 8",
       "[1404915231]", "advert 9", "[1404915231]"
     ]

Hash[arr.each_slice(2).map { |a, b| [a.gsub(/\s+/,'').to_sym, YAML.load(b)] }]
 # => {:advert0=>[1404915231, 1404920520],
 #     :advert4=>[1404915231],
 #     :advert5=>[1404915231],
 #     :advert6=>[1404915231],
 #     :advert7=>[1404915231],
 #     :advert8=>[1404915231],
 #     :advert9=>[1404915231]}

Enumerable#each_slice(2) - 从集合中发送2个项目到Enumerable#map块。现在OP希望 string 转换为符号。因此,我首先使用,删除 strings 之间的空格。它由String#gsub方法完成。我将正则表达式/\s+/作为参数传递给方法#gsub,根据正则表达式,它将找到每个空格并将其替换为空字符串(''。这意味着

"advert 0".gsub(/\s+/,'') # => "advert0"

现在OP想要,所有键都是符号,我应用String#to_sym

"advert0".to_sym # => :advert0

最后,我需要将所有字符串数组转换为数组,因此YAML::load会有所帮助。

 YAML::load "[1404915231, 1404920520]" # => [1404915231, 1404920520]

直到我说的话,会给我们 -

arr.each_slice(2).map { |a, b| [a.gsub(/\s+/,'').to_sym, YAML.load(b)] }
 # => [[:advert0, [1404915231, 1404920520]],
 #     [:advert4, [1404915231]],
 #     [:advert5, [1404915231]],
 #     [:advert6, [1404915231]],
 #     [:advert7, [1404915231]],
 #     [:advert8, [1404915231]],
 #     [:advert9, [1404915231]]]

现在,我将它变为Hash。看看这个Hash[ key, value, ... ] → new_hash

Hash[:a,[1],:b, [2,3]] # => {:a=>[1], :b=>[2, 3]}

希望它有所帮助。

答案 1 :(得分:2)

Arup给出了一个很好的答案,但是如果你正在寻找其他选择,我有一些想法:

解决问题

让我们解决这个问题。我们有ugly_array,我们希望将其偶数元素("advert 0""advert 2")用作哈希中的键,将其奇数元素用作相应的值。我们想要在途中转换键(转换为符号)和值(转换为数字数组)。

创建(临时)哈希

Ruby中一个有用的方法是Hash[],它将使用奇数参数作为键创建哈希,甚至将参数作为值创建。例如:

Hash[ :foo, 1, :bar, 2 ]
# => { :foo => 1, :bar => 2 }

为了将ugly_array中的项目用作单独的参数,我们将*(" splat")放在它之前:

ugly_hash = Hash[ *ugly_array ]

这相当于Hash[ "advert 0", "[1404915231, 1404920520]", "advert 4", ... ],它给了我们这个哈希:

{ "advert 0" => "[1404915231, 1404920520]",
  "advert 4" => "[1404915231]",
  "advert 5" => "[1404915231]",
  # ...
}

转换键

现在我们需要转换键和值。一,钥匙。您想将字符串"advert 0"转换为符号:advert0,我们可以这样做:

"advert 0".gsub(" ", "").to_sym
 # => :advert0

澄清gsub(" ", "")替换所有空格(" ")什么都没有,这有效地删除了它们。我们也可以使用像/\s+/这样的正则表达式,但看起来这里真的不需要额外的灵活性。现在我们有字符串"advert0"to_sym将其转换为符号:advert0

转换值

我们希望将"[1404915231, 1404920520]"之类的字符串转换为[1404915231, 1404920520]之类的数字数组。有很多不同的方法可以做到这一点,但有一种方法不需要使用像JSON或YAML这样的模块:

"[1404915231, 1404920520]".scan(/\d+/).map(&:to_i)
# => [ 1404915231, 1404920520 ]

这有两件事。首先,scan(/\d+/)使用正则表达式在字符串中查找连续数字(\d)的序列,并将它们作为数组返回,产生一个字符串数组:["1404915231", "1404920520"]。然后我们使用map(&:to_i)在每个字符串上调用to_i方法,产生一个数字数组。

转换临时哈希

现在我们知道如何转换键和值,我们可以使用临时Hash并从中构建一个新的转换后的Hash。还有其他方法,但我喜欢Enumerable#each_with_object。假设我们这样做:

ugly_hash.each_with_object({}) do |(key, val), hsh|
  hsh[key] = val
end

在块中,hsh是我们作为{}的参数提供的新的空哈希(each_with_object),并且在每次迭代中我们向其添加val使用密钥key。这会产生:

{ "advert 0" => "[1404915231, 1404920520]",
  "advert 4" => "[1404915231]",
  # ...
}

它看起来完全一样!但你可能会看到,因为我们在块中有keyval,我们可以在使用它们之前对它们进行转换。

将所有这些结合在一起

pretty_hash = Hash[ *ugly_array ].each_with_object({}) do |(key, val), hsh|
  key = key.tr(" ", "").to_sym
  val = val.scan(/\d+/).map(&:to_i)
  hsh[key] = val
end

正如您所看到的,这是相同的代码,除了我们在使用它们向Hash添加元素之前转换keyval的块内部。它为我们提供了我们正在寻找的东西:

p pretty_hash
# => { :advert0 => [ 1404915231, 1404920520 ],
#      :advert4 => [ 1404915231 ],
#      :advert5 => [ 1404915231 ],
#      ...
#    }

除了具有相对可读性且不需要外部库之外,此方法还具有以下独特优势:只对原始数据进行一次迭代,并对每个键和值仅执行一次(复合)操作。

如果您如此倾向,可以将块内部减少到一行,但要注意它如何影响可读性:

pretty_hash = Hash[*ugly_array].each_with_object({}) do |(key, val), hsh|
  hsh[ key.tr(" ", "").to_sym ] = val.scan(/\d+/).map(&:to_i)
end

我希望这有用!

答案 2 :(得分:1)

我以为你想要结果:

{ advert0: [1404915231, 1404920520], advert4: [1404915231] }

也就是说,我认为你写的是advert0 advert1;具体而言,您希望选择与"advert x", arr(此处为arrx)的给定值相对应的04为数组)对和将这些对的集合转换为哈希。如果您不希望这样做,则无需进一步阅读。

您可以按照以下方式执行此操作:

<强>代码

def doit(arr, *vals_at)
  arr.each_slice(2)
     .map {|s1,s2|
       [s1.split.last.to_i,[s1.tr(' ','').to_sym, s2.scan(/\d+/).map(&:to_i)]]}
     .to_h
     .values_at(*vals_at)
     .to_h
end

示例

arr = ["advert 0", "[1404915231, 1404920520]",
       "advert 4", "[1404915231]",
       "advert 5", "[1404915231]"
      ]

(我们不需要剩余的arr。)

doit(arr, 0, 4)
  #=> {:advert0=>[1404915231, 1404920520], :advert4=>[1404915231]}

<强>解释

对于上面arr的值,请:

b = a.each_slice(2)
  #=> #<Enumerator: ["advert 0", "[1404915231, 1404920520]",
  #                  "advert 4", "[1404915231]",
  #                  "advert 5", "[1404915231]"]:each_slice(2)>

c = b.map
  #<Enumerator: #<Enumerator: ["advert 0", "[1404915231, 1404920520]",
  #                            "advert 4", "[1404915231]",
  #                            "advert 5", "[1404915231]"]:each_slice(2)>:map>

c可能会被视为“复合”枚举者。当我们添加一个块时,each会将以下数组的每个值传递给块:

c.to_a
  #=> [["advert 0", "[1404915231, 1404920520]"],
  #    ["advert 4", "[1404915231]"],
  #    ["advert 5", "[1404915231]"]]

请注意

b.to_a == c.to_a #=> true

接下来,

d = c.each { |s1,s2| [s1.split.last.to_i,
                     [s1.tr(' ','').to_sym, s2.scan(/\d+/).map(&:to_i)]] }
  #=> [[0, [:advert0, [1404915231, 1404920520]]],
  #    [4, [:advert4, [1404915231]]],
  #    [5, [:advert5, [1404915231]]]]

请注意,c.each相当于b.map

d == b.map { |s1,s2| [s1.split.last.to_i,
                      [s1.tr(' ','').to_sym, s2.scan(/\d+/).map(&:to_i)]] }
  #=> true

让我们暂停一下,看看这里发生了什么。枚举器c传入块(["advert 0", "[1404915231, 1404920520]"])的第一个值通过消歧分为两个块变量:

s1 #=> "advert 0"
s2 #=> "[1404915231, 1404920520]"

然后:

s1.split.last.to_i
  #=> 0
r = s2.scan(/\d+/)
  #=> ["1404915231", "1404920520"]
r.map(&:to_i)
  #=> [1404915231, 1404920520]

Ergo,传递到块中的第一个元素:

["advert 0", "[1404915231, 1404920520]"]

映射到:

[0,[1404915231, 1404920520]]

类似地,c的第二个和第三个元素分别映射到:

[4=>[:advert4, [1404915231]]

[5=>[:advert5, [1404915231]]

现在我们有d

e = d.to_h # or Hash[d] for Ruby versions < 2.0
  #=> {0=>[:advert0, [1404915231, 1404920520]],
  #    4=>[:advert4, [1404915231]],
  #    5=>[:advert5, [1404915231]]}

啊哈!现在你可以看到为什么我像我一样构建了e

f = e.values_at(0,4)
  #=> [[:advert0, [1404915231, 1404920520]], [:advert4, [1404915231]]]

f.to_h
  #=> {:advert0=>[1404915231, 1404920520], :advert4=>[1404915231]}