代码之家  ›  专栏  ›  技术社区  ›  t56k

从嵌套哈希中删除所有类似的键

  •  2
  • t56k  · 技术社区  · 6 年前

    给定这样的散列:

    {
      id: 1,
      name: "test",
      children: [
        { id: 1, name: "kid 1" },
        { id: 2, name: "kid 2" }
      ]
    }
    

    如何删除 全部的 id 递归键?

    2 回复  |  直到 6 年前
        1
  •  5
  •   Schwern    6 年前

    您可以编写一个函数递归遍历散列和数组。

    def delete_recursively(thing, key_to_delete)
      case thing
      when Hash
        # Delete the key
        thing.delete(key_to_delete)
    
        # Recurse into each remaining hash value.
        thing.each_value do |value|
          delete_recursively(value, key_to_delete)
        end
      when Array
        # Recurse into each value of the array.
        thing.each do |value|
          delete_recursively(value, key_to_delete)
        end
      end
    end
    

    根据需要,可以将其扩展为包括其他数据类型。

        2
  •  0
  •   Cary Swoveland    6 年前

    这当然需要一个递归的解决方案。以下方法不会对原始哈希进行变异。

    代码

    def recurse(obj, key_to_delete)
      case obj
      when Array
        obj.map { |e| recurse(e, key_to_delete) }
      else # hash
        obj.reject { |k,_| k == key_to_delete }.transform_values do |v|
          case v
          when Hash, Array
            recurse(v, key_to_delete)
          else
            v
          end
        end
      end
    end
    

    例子

    h = { id: 1, name: "test", children: [
            { id: 1, name: "kid 1" }, { id: 2, name: "kid 2", grandkids: [
                { id: 3, name: "gkid1" }] }
          ]
        }
    

    recurse(h, :id)
      #=> { :name=>"test", :children=>[
      #       {:name=>"kid 1"}, {:name=>"kid 2", :grandkids=>[{:name=>"gkid1"}]}
      #     ]
      #   }          
    

    解释

    递归方法执行的操作总是很难解释。根据我的经验,最好的方法是在代码中添加puts语句。然而,这本身是不够的,因为在查看输出时,很难跟踪获得特定结果并将其传递给自身或返回给自身版本的递归级别。解决方法是缩进和取消缩进结果,这就是我在下面所做的。请注意,我构造代码的方式和我使用的少数助手方法是相当通用的,因此这种方法可以用于检查由其他递归方法执行的操作。

    INDENT = 8
    @col = -INDENT
    def indent; @col += INDENT; end
    def undent; @col -= INDENT; end
    def pu(s); print " "*@col; puts s; end
    def puhline; pu('-'*(70-@col)); end 
    

    def recurse(obj, key_to_delete)      
      begin                                                           # rem
        indent                                                        # rem
        puhline                                                       # rem
        pu "passed obj = #{obj}, key_to_delete = #{key_to_delete}"    # rem   
        case obj
        when Array
          pu "processing Array..."                                    # rem
          obj.map do |e|
            pu "  calling recurse(#{e}, #{key_to_delete})..."         # rem
            recurse(e, key_to_delete)
          end
        else # hash
          obj.reject { |k,_| k == :id }.
              tap { |h| pu "  obj with :id removed=#{h}" }.           # rem
              transform_values do |v| 
                pu "  in tranform_values, v=#{v}"                     # rem
                case v
                when Hash, Array
                  pu "    v is Hash or Arrary"                        # rem
                  pu "    calling recurse(#{v}, #{key_to_delete})..." # rem
                  recurse(v, key_to_delete)
                else
                  pu "    keeping the literal v"                      # rem
                  v
                end
              end
        end.tap { |obj| pu "returning #{obj}" }        
      ensure
        puhline                                                       # rem
        undent
      end
    end
    

    recurse(h, :id)
    ----------------------------------------------------------------------
    passed obj = {:id=>1, :name=>"test", :children=>[{:id=>1, :name=>"kid 1"},
             {:id=>2, :name=>"kid 2", :grandkids=>[{:id=>3, :name=>"gkid1"}]}]},
           key_to_delete = id
      obj with :id removed={:name=>"test", :children=>[{:id=>1, :name=>"kid 1"},
        {:id=>2, :name=>"kid 2", :grandkids=>[{:id=>3, :name=>"gkid1"}]}]}
      in tranform_values, v=test
        keeping the literal v
      in tranform_values, v=[{:id=>1, :name=>"kid 1"},
        {:id=>2, :name=>"kid 2", :grandkids=>[{:id=>3, :name=>"gkid1"}]}]
        v is Hash or Arrary
        calling recurse([{:id=>1, :name=>"kid 1"},
          {:id=>2, :name=>"kid 2", :grandkids=>[{:id=>3, :name=>"gkid1"}]}], id)...
            --------------------------------------------------------------
            passed obj = [{:id=>1, :name=>"kid 1"},
                     {:id=>2, :name=>"kid 2", :grandkids=>[{:id=>3, :name=>"gkid1"}]}],
                   key_to_delete = id
            processing Array...
              calling recurse({:id=>1, :name=>"kid 1"}, id)...
                    ------------------------------------------------------
                    passed obj = {:id=>1, :name=>"kid 1"}, key_to_delete = id
                      obj with :id removed={:name=>"kid 1"}
                      in tranform_values, v=kid 1
                        keeping the literal v
                    returning {:name=>"kid 1"}
                    ------------------------------------------------------
              calling recurse({:id=>2, :name=>"kid 2",
                :grandkids=>[{:id=>3, :name=>"gkid1"}]}, id)...
                    ------------------------------------------------------
                    passed obj = {:id=>2, :name=>"kid 2", :grandkids=>
                             [{:id=>3, :name=>"gkid1"}]},
                           key_to_delete = id
                      obj with :id removed={:name=>"kid 2", :grandkids=>
                        [{:id=>3, :name=>"gkid1"}]}
                      in tranform_values, v=kid 2
                        keeping the literal v
                      in tranform_values, v=[{:id=>3, :name=>"gkid1"}]
                        v is Hash or Arrary
                        calling recurse([{:id=>3, :name=>"gkid1"}], id)...
                            ----------------------------------------------
                            passed obj = [{:id=>3, :name=>"gkid1"}],
                                   key_to_delete = id
                            processing Array...
                              calling recurse({:id=>3, :name=>"gkid1"}, id)...
                                    --------------------------------------
                                    passed obj = {:id=>3, :name=>"gkid1"},
                                           key_to_delete = id
                                      obj with :id removed={:name=>"gkid1"}
                                      in tranform_values, v=gkid1
                                        keeping the literal v
                                    returning {:name=>"gkid1"}
                                    --------------------------------------
                            returning [{:name=>"gkid1"}]
                            ----------------------------------------------
                    returning {:name=>"kid 2", :grandkids=>[{:name=>"gkid1"}]}
                    ------------------------------------------------------
            returning [{:name=>"kid 1"},
                       {:name=>"kid 2", :grandkids=>[{:name=>"gkid1"}]}]
            --------------------------------------------------------------
    returning {:name=>"test", :children=>[{:name=>"kid 1"},
                 {:name=>"kid 2", :grandkids=>[{:name=>"gkid1"}]}]}
    ----------------------------------------------------------------------
      #=> {:name=>"test", :children=>[{:name=>"kid 1"},
          {:name=>"kid 2", :grandkids=>[{:name=>"gkid1"}]}]} 
    

    推荐文章