group_by 在 ruby 中有很多标签的项目列表

group_by a list of items which have many tags in ruby

我有一个对象列表,其中可能有 0 个或多个与之相关联的标签。在最简单的情况下,我有一个对象散列和一个标签列表:

collection = {'object1': ['tag1', 'tag2'], 'object2': ['tag1'], 'object3
': ['tag2'], 'object4': []}

所以每个对象可以有很多标签。

我想 group_by 每个标签和 return 这个:

{'tag1': ['object1', 'object2'], 'tag2': ['object1, 'object3'], nil: ['object4']}

(我不介意我们使用 nil 或空字符串或任何模型化“无标签”的对象)

应该这样做:

  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

试试下面的方法:

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]}

使用 #enumerable#wit_object with 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

(*) If obj is specified, this single object will be used for all default values.

collection = {
  'object1': ['tag1', 'tag2'], 'object2': ['tag1'], 'object3': ['tag2'],
  'object4': []
}
  #=> {:object1=>["tag1", "tag2"], :object2=>["tag1"], :object3=>["tag2"],
  #    :object4=>[]}

我想说两种最 Ruby-like 的方法如下。

#1

collection.each_with_object({}) do |(k,v),h|
  v = [nil] if v.empty?
  v.each { |s| (h[s] ||= []) << k }
end
  #=> {"tag1"=>[:object1, :object2], "tag2"=>[:object1, :object3], 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,v),h|
  v = [nil] if v.empty?
  v.each { |s| h[s] << k }
end
  #=> {"tag1"=>[:object1, :object2], "tag2"=>[:object1, :object3], nil=>[:object4]}

这使用了 Hash::new 的第三种形式,它创建了一个 默认过程 。如果

h = Hash.new(Hash.new { |h,k| h[k] = [] }

h没有密钥k,

h[k] << v

导致要执行的块,之后 h[k] #=> [],然后是语句

h[k] << v

已完成。