【问题标题】:Ruby: Invert a hash to also preserve non unique valuesRuby:反转哈希以保留非唯一值
【发布时间】:2015-04-12 06:47:10
【问题描述】:

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

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

【问题讨论】:

  • @mikej 请再次将此问题与您引用的问题进行比较。我不明白这个问题与前一个问题重复的说法。上一个问题需要直接使用invert。此处不能使用。
  • @Cary,对不起,你是对的。已经重新开放。我认为这个问题的标题可以改进,但是这个例子确实很清楚。
  • "Stack Overflow question checklist" 是一本好书。我们需要的不仅仅是需求,我们需要看到解决问题的尝试。包含需求的问题只会让人觉得您是在要求我们为您编写代码,这是题外话。

标签: ruby hash


【解决方案1】:

试试这个:

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

【讨论】:

  • Object#extend 会炸掉 ruby​​ 方法缓存,不建议用于运行时代码。
  • 非常好,JJ。请注意,您不需要self.。或许值得注意的是...with_object(Hash.new { |h,k| h[k] = [] }{ |v| result &lt;&lt; key } 是另一种选择。顺便说一句,我发现...(result[v] ||= []) &lt;&lt; key 通常是两者中更快的。 include HashReverser 可能更清楚。
  • 请解释您的代码以及为什么它是推荐的解决方案。我们正在努力进行教育,而不仅仅是解决代码的直接问题。
【解决方案2】:

另一种解决方案:

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

【讨论】:

    【解决方案3】:

    我确实更喜欢@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"]} 
    

    【讨论】:

      猜你喜欢
      • 2015-05-10
      • 2011-02-15
      • 2018-02-04
      • 1970-01-01
      • 1970-01-01
      • 2014-04-09
      • 1970-01-01
      • 2020-10-09
      • 1970-01-01
      相关资源
      最近更新 更多