问题描述
我有一个对象列表,其中可能有0个或多个与之关联的标签。在最简单的情况下,我有一个对象哈希和一个标记列表:
collection = {'object1': ['tag1','tag2'],'object2': ['tag1'],'object3
': ['tag2'],'object4': []}
所以每个对象可以有很多标签。
我想对每个标签进行分组并返回:
{'tag1': ['object1','object2'],'tag2': ['object1,'object3'],nil: ['object4']}
(我不介意我们使用nil还是空字符串,或者任何模型化“无标签”的对象)
解决方法
请尝试以下操作:
collection = {'object1': ['tag1','tag2'],'object2': ['tag1'],'object3': ['tag2'],'object4': []}
result = {}
collection.map do |object,tags|
if tags.any?
tags.each { |tag| result[tag] ||= []; result[tag] << object }
else
result[nil] ||= []; result[nil] << object
end
end
结果应为:
2.6.3 :147 > result
=> {"tag1"=>[:object1,:object2],"tag2"=>[:object1,:object3],nil=>[:object4]}
,
collection = {
'object1': ['tag1','object4': []
}
#=> {:object1=>["tag1","tag2"],:object2=>["tag1"],:object3=>["tag2"],# :object4=>[]}
我会说两种最类似于Ruby的方式如下。
#1
collection.each_with_object({}) do |(k,v),h|
v = [nil] if v.empty?
v.each { |s| (h[s] ||= []) << k }
end
#=> {"tag1"=>[:object1,nil=>[:object4]}
行
(h[s] ||= []) << k
扩展到
(h[s] = h[s] || []) << k
因此,如果h
没有密钥s
,则变成
(h[s] = nil || []) << k
(h[s] = []) << k
h[s] = [k]
#2
collection.each_with_object(Hash.new { |h,k| h[k] = [] }) do |(k,h|
v = [nil] if v.empty?
v.each { |s| h[s] << k }
end
#=> {"tag1"=>[:object1,nil=>[:object4]}
这使用Hash::new的第三种形式来创建默认proc 。如果
h = Hash.new(Hash.new { |h,k| h[k] = [] }
和h
没有密钥k
,
h[k] << v
导致要执行的块,然后是h[k] #=> []
,然后是语句
h[k] << v
完成。
,这应该做到:
results = Hash.new
collection.map do |k,v|
v.each do |tag|
results[tag.to_sym] ||= []
results[tag.to_sym] << k unless results[tag.to_sym].include? k
end
if v.empty?
results[nil] ||= []
results[nil] << k unless results[nil].include? k
end
end
results
,
使用#enumerable#wit_object(*)和Hash#new将默认的空数组作为对象:
res = collection.each_with_object(Hash.new([])) do |(obj,tags),h|
tags.each { |tag| h[tag] += [obj] }
h[nil] += [obj] if tags.empty?
end
(*)如果指定了obj,则该单个对象将用于所有默认值。