如何递归地从 Ruby 中的 json 结构的键中删除一个或多个字符

How to remove a character or characters from the keys of a json structure in Ruby recursively

所以基本上有这样的结构:

{
    "i$tems": {
        "it$em": [
            {
                "batt$ers": {
                    "ba$tter": [
                        {
                            "i$d": "1001",
                            "t$ype": "Regular"
                        },
                        {
                            "i$d": "1002",
                            "type": "Chocolate"
                        },
                        {
                            "i$d": "1003",
                            "t$ype": "Blueberry"
                        },
                        {
                            "i$d": "1004",
                            "t$ype": "Devil's Food"
                        }
                    ]
                },
                "$id": "0001",
                "$name": "Cake",
                "$ppu": 0.55,
                "$topping": [
                    {
                        "i$d": "5001",
                        "t$ype": "None"
                    },
                    {
                        "i$d": "5002",
                        "t$ype": "Glazed"
                    },
                    {
                        "i$d": "5005",
                        "t$ype": "Sugar"
                    },
                    {
                        "i$d": "5007",
                        "t$ype": "Powdered Sugar"
                    },
                    {
                        "i$d": "5006",
                        "t$ype": "Chocolate with Sprinkles"
                    },
                    {
                        "i$d": "5003",
                        "t$ype": "Chocolate"
                    },
                    {
                        "i$d": "5004",
                        "t$ype": "Maple"
                    }
                ],
                "ty$pe": "donut"
            }
        ]
    } 
}

并想要这样的东西:

{
    "items": {
        "item": [
            {
                "batters": {
                    "batter": [
                        {
                            "id": "1001",
                            "type": "Regular"
                        },
                        {
                            "id": "1002",
                            "type": "Chocolate"
                        },
                        {
                            "id": "1003",
                            "type": "Blueberry"
                        },
                        {
                            "id": "1004",
                            "type": "Devil's Food"
                        }
                    ]
                },
                "id": "0001",
                "name": "Cake",
                "ppu": 0.55,
                "topping": [
                    {
                        "id": "5001",
                        "type": "None"
                    },
                    {
                        "id": "5002",
                        "type": "Glazed"
                    },
                    {
                        "id": "5005",
                        "type": "Sugar"
                    },

                    {
                        "id": "5007",
                        "type": "Powdered Sugar"
                    },
                    {
                        "id": "5006",
                        "type": "Chocolate with Sprinkles"
                    },
                    {
                        "id": "5003",
                        "type": "Chocolate"
                    },
                    {
                        "id": "5004",
                        "type": "Maple"
                    }
                ],
                "type": "donut"
            }
        ]
    }
}

在Ruby

基本上我想出了这个代码:

#!/usr/bin/env ruby

require 'yaml'
require 'json'

def iterate(data)
  if data.is_a?(Array)
    return data.map { |i| iterate(i) }
  elsif data.is_a?(Hash)
    new_h = Hash[data]
    data.each do |k, v|
      if k =~ /$/
        new_key = k.delete("$")
        new_h.delete(k)
        if v.is_a?(Array) || v.is_a?(Hash)
          new_h[new_key] = iterate(v)
        else
          new_h[new_key] = v
        end
      else
        if v.is_a?(Array) || v.is_a?(Hash)
          new_h[k] = iterate(v)
        else
          new_h[k] = v
        end
      end
    end
    return new_h
  end
end

data = YAML.load(File.read('nested.json'))
puts JSON.dump(iterate(data))

这是一个通用的 map_keys 函数,它在哈希的键上递归地运行一些块。

def map_keys(hash, &blk)
  hash.each_with_object({}) do |(key, val), new_hash|        
    new_hash[blk.call(key)] = if val.is_a?(Hash)
      map_keys(val, &blk)
    else
      val
    end
  end
end

它returns 一个新的散列,修改了键。在您的情况下,您可以这样使用它:

new_data = map_keys(data) do |key|
  key.gsub("$", '')
end

正在编辑

刚刚注意到您的数据中有数组。这是处理该问题的修改版本:

def map_keys(hash_or_array, &blk)
  if hash_or_array.is_a?(Hash)
    hash = hash_or_array
    hash.each_with_object({}) do |(key, val), new_hash|        
      new_hash[blk.call(key)] = if [Array, Hash].include?(val.class)
        map_keys(val, &blk)
      else
        val
      end
    end
  elsif hash_or_array.is_a?(Array)
    array = hash_or_array
    array.map { |elem| map_keys(elem, &blk) }
  end
end

顺便说一句,修改它以处理值而不是键很容易:

def map_values(hash_or_array, &blk)
  if hash_or_array.is_a?(Hash)
    hash = hash_or_array
    hash.each_with_object({}) do |(key, val), new_hash|        
      new_hash[key] = if [Array, Hash].include?(val.class)
        map_keys(val, &blk)
      else
        blk.call val
      end
    end
  elsif hash_or_array.is_a?(Array)
    array = hash_or_array
    array.map { |elem| map_keys(elem, &blk) }
  end
end

如果唯一要删除的美元符号字符是键名称中的美元符号字符,无论是符号还是字符串,您可以执行以下操作。

def clean_up(obj)
  case obj
  when Hash
    obj.each_with_object({}) do |(k,v),h|
      key =
      case k
      when String then k.delete('$')
      when Symbol then k.to_s.delete('$').to_sym
      else k
      end
      h[key] = clean_up(v)
    end
  when Array
    obj.each_with_object([]) { |e,a| a << clean_up(e) }
  else
    obj
  end
end

假设

h =
{ "i$tems": {
    "it$em": [
      { "batt$ers": {
          "ba$tter": [
            { "i$d": "1001", "t$ype": "Regular" },
            { "i$d": "1002", type: "Choc$olate" }
          ]
        },
        "i$d": "0001",
        42=>[
          { "i$d": "5001", "t$ype": "None" }, 
          { "str$ing"=>"5002", "t$ype": "Glazed"}
        ],
        "ty$pe": "donut"
      }
    ]
  }
}

请注意,我简化了问题中的散列,还更改了一些键和值。那么,

clean_up h
  #=> { :items=>{
          :item=>[
            { :batters=>{
                :batter=>[
                  { :id=>"1001", :type=>"Regular" },
                  { :id=>"1002", :type=>"Choc$olate" }
                ]
              },
              :id=>"0001",
              42=>[
                { :id=>"5001", :type=>"None" },
                { "string"=>"5002", :type=>"Glazed" }
              ],
              :type=>"donut"
            }
          ]
        }
      }

如果保证(如示例中)唯一包含美元符号的符号或字符串是哈希键,则可以简化为以下内容。

require 'json'

JSON.parse(h.to_json.delete('$'))