代码之家  ›  专栏  ›  技术社区  ›  Alex Harvey

将数组和/或标量的哈希转换为标量组合的数组

  •  4
  • Alex Harvey  · 技术社区  · 8 年前

    我觉得这对某些人来说一定是一个很容易的问题,而我花了大量的时间试图找到一个解决方案,却没有找到一个我喜欢的。

    我不会试图用文字说明我需要什么,只会给出一些示例输入及其预期输出,作为Rspec代码:

    方法是:

    def explode(hash)
      ...
    end
    

    以及规格:

    describe '#explode' do
      it do
        expect(explode({:a => 1, :b => 2})).
          to eq [[:a, 1, :b, 2]]
      end
    
      it do
        expect(explode({:a => 1, :b => [2,3,4]})).
          to eq [
            [:a, 1, :b, 2],
            [:a, 1, :b, 3],
            [:a, 1, :b, 4]
          ]
      end
    
      it do
        expect(explode({:a => [1,2], :b => [3,4]})).
          to eq [
            [:a, 1, :b, 3],
            [:a, 2, :b, 3],
            [:a, 1, :b, 4],
            [:a, 2, :b, 4]
          ]
      end
    
      it do
        expect(explode({:a => 1, :b => [2,3], :c => [4,5,6]})).
          to eq [
            [:a, 1, :b, 2, :c, 4],
            [:a, 1, :b, 3, :c, 4],
            [:a, 1, :b, 2, :c, 5],
            [:a, 1, :b, 3, :c, 5],
            [:a, 1, :b, 2, :c, 6],
            [:a, 1, :b, 3, :c, 6]
          ]
      end
    end
    

    也欢迎使用Ruby以外的其他语言的解决方案。

    3 回复  |  直到 8 年前
        1
  •  2
  •   Sagar Pandya    8 年前

    Array#product 看起来很适合这里。

    h1 = {:a => 1, :b => 2}
    h2 = {:a => 1, :b => [2,3,4]}
    h3 = {:a => [1,2], :b => [3,4]}
    h4 = {:a => 1, :b => [2,3], :c => [4,5,6]}
    
    def explode hash
      a, *b = hash.transform_values { |v| [*v] }.values.unshift
      a.product(*b).map { |ar| hash.keys.zip(ar).flatten }.sort_by(&:last)
    end
    
    p explode h1  
     #[[:a, 1, :b, 2]]
    p explode h2
     #[[:a, 1, :b, 2],
     # [:a, 1, :b, 3],
     # [:a, 1, :b, 4]]
    
    p explode h3
     #[[:a, 1, :b, 3],
     # [:a, 2, :b, 3],
     # [:a, 1, :b, 4],
     # [:a, 2, :b, 4]]
    
    p explode h4
     #[[:a, 1, :b, 2, :c, 4],
     # [:a, 1, :b, 3, :c, 4],
     # [:a, 1, :b, 2, :c, 5],
     # [:a, 1, :b, 3, :c, 5],
     # [:a, 1, :b, 2, :c, 6],
     # [:a, 1, :b, 3, :c, 6]]
    

    为了使我的方法发挥作用,我必须重新映射这些值,使它们都是数组,这并不理想。但我仍然发布了这个答案,因为它可能会给你或其他人一个起点。

        2
  •  2
  •   Cary Swoveland    8 年前

    您可以使用 Array#product 两次

    def explode(hash)
      first, *rest = hash.map { |k,v| [k].product([*v]) }
      first.product(*rest).map(&:flatten)
    end
    
    h = { :a =>1, :b =>[2,3], :c =>[4,5,6] }    
    explode h
      #=> [[:a, 1, :b, 2, :c, 4], [:a, 1, :b, 2, :c, 5], [:a, 1, :b, 2, :c, 6], 
      #    [:a, 1, :b, 3, :c, 4], [:a, 1, :b, 3, :c, 5], [:a, 1, :b, 3, :c, 6]]
    

    请注意,对于 h 在上面

    first, *rest = h.map { |k,v| [k].product([*v]) }
      #=> [[[:a, 1]], [[:b, 2], [:b, 3]], [[:c, 4], [:c, 5], [:c, 6]]]
    first
      #=> [[:a, 1]]
    rest
      #=> [[[:b, 2], [:b, 3]], [[:c, 4], [:c, 5], [:c, 6]]]
    

    和

    first.product(*rest)
      #=> [[[:a, 1], [:b, 2], [:c, 4]], [[:a, 1], [:b, 2], [:c, 5]],
      #    [[:a, 1], [:b, 2], [:c, 6]], [[:a, 1], [:b, 3], [:c, 4]],
      #    [[:a, 1], [:b, 3], [:c, 5]], [[:a, 1], [:b, 3], [:c, 6]]]
    

    请注意 [*1] #=> [1] ,则, [*:a] #=> [:a] 和 [*[1,2]] #=> [1,2] ,意思是 [*k] 转换标量 k 到包含该元素的数组,并 [*k] 等于 k 如果 k 是一个数组。

        3
  •  1
  •   Alex Harvey    8 年前

    因为我必须让它为Ruby工作<2.4(没有transform\u值的情况下)-而且,由于我不需要对数组进行排序,我最终得到了:

    def explode(hash)
      hash.each do |k,v|
        if not hash[k].is_a?(Array)
          hash[k] = [hash[k]]
        end
      end
      a, *b = hash.values.unshift
      a.product(*b).map do |arr|
        hash.keys.zip(arr).flatten
      end
    end