所以我正在开发一个我有一系列哈希的项目:
[{:year=>2016, :month=>12, :account_id=>133, :price=>5},
{:year=>2016, :month=>11, :account_id=>134, :price=>3},
{:year=>2016, :month=>11, :account_id=>135, :price=>0},
{:year=>2015, :month=>12, :account_id=>145, :price=>4},
{:year=>2015, :month=>12, :account_id=>163, :price=>11}]
基本上我想把它浓缩成表格:
{ 2016 => { 12 => { 1 => {:account_id=>133, :price=>5}},
11 => { 1 => {:account_id=>134, :price=>3},
2 => {:account_id=>135, :price=>0}}},
2015 => { 12 => { 1 => {:account_id=>145, :price=>4},
2 => {:account_id=>163, :price=>11}}}}
但是在我完成这件事的时候我遇到了真正的麻烦:
data_array = data_array.group_by{|x| x[:year]}
data_array.each{|x| x.group_by{|y| y[:month]}}
但这似乎不起作用,我得到一个错误,表示没有将Symbol隐式转换为整数。
任何帮助,了解我出错的地方和做什么都将非常感激。
答案 0 :(得分:4)
这是一个更长但可能更好的解决方案,有3个辅助方法:
class Array
# Remove key from array of hashes
def remove_key(key)
map do |h|
h.delete(key)
h
end
end
# Group hashes by values for given key, sort by value,
# remove key from hashes, apply optional block to array of hashes.
def to_grouped_hash(key)
by_key = group_by { |h| h[key] }.sort_by { |value, _| value }
by_key.map do |value, hashes|
hashes_without = hashes.remove_key(key)
new_hashes = block_given? ? yield(hashes_without) : hashes_without
[value, new_hashes]
end.to_h
end
# Convert array to indexed hash
def to_indexed_hash(first = 0)
map.with_index(first) { |v, i| [i, v] }.to_h
end
end
然后您的脚本可以写成:
data.to_grouped_hash(:year) do |year_data|
year_data.to_grouped_hash(:month) do |month_data|
month_data.to_indexed_hash(1)
end
end
它不需要Rails或Activesupport,并返回:
{2015=>
{12=>
{1=>{:account_id=>145, :balance=>4}, 2=>{:account_id=>163, :balance=>11}}},
2016=>
{11=>
{1=>{:account_id=>134, :balance=>3}, 2=>{:account_id=>135, :balance=>0}},
12=>{1=>{:account_id=>133, :price=>5}}}}
可以使用细化来避免污染Array类。
# require 'active_support/core_ext/hash'
# ^ uncomment in plain ruby script.
data.group_by{|h| h[:year]}
.map{|year, year_data|
[
year,
year_data.group_by{|month_data| month_data[:month]}.map{|month, vs| [month, vs.map.with_index(1){|v,i| [i,v.except(:year, :month)]}.to_h]}
.to_h]
}.to_h
它使用ActiveSupport中的Hash#except。
输出:
{
2016 => {
12 => {
1 => {
:account_id => 133,
:price => 5
}
},
11 => {
1 => {
:account_id => 134,
:balance => 3
},
2 => {
:account_id => 135,
:balance => 0
}
}
},
2015 => {
12 => {
1 => {
:account_id => 145,
:balance => 4
},
2 => {
:account_id => 163,
:balance => 11
}
}
}
}
答案 1 :(得分:1)
知道我迟到了,但这个问题有一个漂亮的递归结构,值得一看。
输入是哈希数组和要分组的键列表。
对于基本情况,键列表为空。只需将哈希数组转换为索引值哈希即可。
否则,使用列表中的第一个键来累积具有相应输入值的哈希作为键,每个键都映射到已删除该键的哈希列表。这些列表中的每一个都只是使用剩余键的相同问题的较小实例!所以经常照顾他们。
def group_and_index(a, keys)
if keys.empty?
a.each_with_object({}) {|h, ih| ih[ih.size + 1] = h }
else
r = Hash.new {|h, k| h[k] = [] }
a.each {|h| r[h.delete(keys[0])].push(h) }
r.each {|k, a| r[k] = group_and_index(a, keys[1..-1]) }
end
end
如果任何输入哈希中缺少某个键,则将使用nil
。请注意,此功能会修改原始哈希值。如果不需要,请致电a.map{|h| h.clone}
。要获得示例结果:
group_and_index(array_of_hashes, [:year, :month])
答案 2 :(得分:0)
//Runnable runnable = new Runnable() {
// @Override
// public void run() {
for (int i = 0; i < data.size(); i++) {
dbOpen();
Map<String, Object> map = data.get(i);
ContentValues cv = new ContentValues();
cv.put("name", (String) map.get("name"));
cv.put("version", (int) map.get("version"));
cv.put("uuid", (String) map.get("uuid"));
cv.put("measure", (String) map.get("measure"));
database.insert("nomenclature", null, cv);
dbClose();
}
// }
// };
// new Thread(runnable).start();
这使用方法Hash#dig以及Hash#update(又名arr = [{:year=>2016, :month=>12, :account_id=>133, :price=>5},
{:year=>2016, :month=>11, :account_id=>134, :price=>3},
{:year=>2016, :month=>11, :account_id=>135, :price=>0},
{:year=>2015, :month=>12, :account_id=>145, :price=>4},
{:year=>2015, :month=>12, :account_id=>163, :price=>11}]
arr.each_with_object({}) do |g,h|
f = h.dig(g[:year], g[:month])
counter = f ? f.size+1 : 1
h.update(g[:year]=>{ g[:month]=>
{ counter=>{ account_id: g[:account_id], price: g[:price] } } }) { |_yr,oh,nh|
oh.merge(nh) { |_mon,ooh,nnh| ooh.merge(nnh) } }
end
#=> {2016=>{12=>{1=>{:account_id=>133, :price=>5}},
# 11=>{1=>{:account_id=>134, :price=>3},
# 2=>{:account_id=>135, :price=>0}}
# },
# 2015=>{12=>{1=>{:account_id=>145, :price=>4},
# 2=>{:account_id=>163, :price=>11}}
# }
# }
)和Hash#merge的形式,它们使用块来确定两者中存在的键的值哈希被合并。 (有关详细信息,请参阅文档。)请注意,在两个不同的级别上存在此类块。例如,如果
merge!
正在合并,该块将确定{ 2016=>{ 11=>{ {1=>{:account_id=>133, :price=>5 } } } } }
{ 2016=>{ 11=>{ {2=>{:account_id=>135, :price=>0 } } } } }
的值。这涉及合并两个哈希
2016
会调用内部块来确定{ 11=>{ {1=>{:account_id=>133, :price=>5 } } } }
{ 11=>{ {2=>{:account_id=>135, :price=>0 } } } }
的值。
答案 3 :(得分:-1)
这是一个简单的双线
h = Hash.new { |h,k| h[k] = Hash.new { |h,k| h[k] = [] }}
ary.each { |each| h[each.delete(:year)][each.delete(:month)] << each }
注意,这会修改输入,但我认为在转换后你对原始输入不感兴趣。
h
的价值
{
2016=>{12=>[{:account_id=>133, :price=>5}], 11=>[{:account_id=>134, :price=>3}, {:account_id=>135, :price=>0}]},
2015=>{12=>[{:account_id=>145, :price=>4}, {:account_id=>163, :price=>11}]}
}
您可以使用
访问h
中的值
h[2016][11][1] # => {:account_id=>135, :price=>0}