Ruby:反转哈希以保留非唯一值

时间:2015-02-11 18:49:37

标签: ruby hash

我有一个看起来像这样的哈希:

{"a" => [1, 2, 3], "b" => [4, 5, 6], "c" => [3, 4, 5], "d" => [7, 2, 3]}

我想要做的是使用包含它的所有键的数组来对所有现有值进行散列,例如将上述内容改为:

{1 => ["a"], 2 => ["a", "d"], 3 => ["a", "c", "d"], 4 => ["b", "c"]}

3 个答案:

答案 0 :(得分:3)

试试这个:

module HashReverser
  def invert_map
    each_with_object({}) do |(key, value), result|
      value.each { |v| (result[v] ||= []) << key }
    end
  end
end

original = {"a" => [1, 2, 3], "b" => [4, 5, 6], "c" => [3, 4, 5]}

original.extend(HashReverser).invert_map # => {1=>["a"], 2=>["a"], 3=>["a", "c"], 4=>["b", "c"], 5=>["b", "c"], 6=>["b"]}

答案 1 :(得分:2)

我更喜欢@Jikku的解决方案,但总有另一种方式。这是一个。 [编辑:我认为这与@Chris的解决方案非常接近。我将把它留给最后一行,这有点不同。]

<强>代码

def inside_out(h)
  g = h.flat_map { |s,a| a.product([s]) }
       .group_by(&:first)
  g.merge(g) { |_,a| a.map(&:last) }
end

示例

h = {"a" => [1, 2, 3], "b" => [4, 5, 6], "c" => [3, 4, 5], "d" => [7, 2, 3]}

inside_out(h)
  #=> {1=>["a"], 2=>["a", "d"], 3=>["a", "c", "d"], 4=>["b", "c"],
  #    5=>["b", "c"], 6=>["b"], 7=>["d"]} 

<强>解释

对于上面的h

a = h.flat_map { |s,a| a.product([s]) }
  #=> [[1, "a"], [2, "a"], [3, "a"], [4, "b"], [5, "b"], [6, "b"],
  #    [3, "c"], [4, "c"], [5, "c"], [7, "d"], [2, "d"], [3, "d"]] 
g = a.group_by(&:first)
  #=> {1=>[[1, "a"]], 2=>[[2, "a"], [2, "d"]],
  #    3=>[[3, "a"], [3, "c"], [3, "d"]],
  #    4=>[[4, "b"], [4, "c"]],
  #    5=>[[5, "b"], [5, "c"]],
  #    6=>[[6, "b"]],
  #    7=>[[7, "d"]]} 
g.merge(g) { |_,a| a.map(&:last) }
  #=> {1=>["a"], 2=>["a", "d"], 3=>["a", "c", "d"], 4=>["b", "c"],
  #    5=>["b", "c"], 6=>["b"], 7=>["d"]} 

答案 2 :(得分:1)

另一种解决方案:

# Given
h = {"a" => [1, 2, 3], "b" => [4, 5, 6], "c" => [3, 4, 5], "d" => [7, 2, 3]}

h.flat_map {|k, v| v.product [k]}.group_by(&:first).each_value {|v| v.map! &:last }

或者:

h.flat_map {|k, v| v.product [k]}.reduce({}) {|o, (k, v)| (o[k] ||= []) << v; o}

这里的想法是我们使用Array#product创建一个倒置的单键值对列表:

product = h.flat_map {|k, v| v.product([k]) }
# => [[1, "a"], [2, "a"], [3, "a"], [4, "b"], [5, "b"], [6, "b"], [3, "c"], [4, "c"], [5, "c"], [7, "d"], [2, "d"], [3, "d"]]

按每对中第一项的值对它们进行分组:

groups = product.group_by(&:first)
# => {1=>[[1, "a"]], 2=>[[2, "a"], [2, "d"]], 3=>[[3, "a"], [3, "c"], [3, "d"]], 4=>[[4, "b"], [4, "c"]], 5=>[[5, "b"], [5, "c"]], 6=>[[6, "b"]], 7=>[[7, "d"]]}

然后将值转换为每对中最后一个值的列表:

result = groups.each_value {|v| v.map! &:last }
# => {1=>["a"], 2=>["a", "d"], 3=>["a", "c", "d"], 4=>["b", "c"], 5=>["b", "c"], 6=>["b"], 7=>["d"]}