我有一个看起来像这样的丑陋数组
["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]}
由于
答案 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]",
# ...
}
它看起来完全一样!但你可能会看到,因为我们在块中有key
和val
,我们可以在使用它们之前对它们进行转换。
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添加元素之前转换key
和val
的块内部。它为我们提供了我们正在寻找的东西:
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
(此处为arr
和x
)的给定值相对应的0
(4
为数组)对和将这些对的集合转换为哈希。如果您不希望这样做,则无需进一步阅读。
您可以按照以下方式执行此操作:
<强>代码强>
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]}