具有层次结构

时间:2016-07-05 12:53:54

标签: ruby json algorithm hash tree

我的层次结构中有一个哈希值,如['country', 'city', 'street', ...]  谁是我树的最后一个儿子的头。

我的数据看起来像

{"country": "france", "city": "paris", "street": "champs elysee"},
{"country": "france", "city": "Lyon", "street": "lyon street"},
{"country": "italy", "city": "rome", "street": "rome street1"},
...

我想将其转换为像

这样的树
[
  {
    "france" => [
      {
         "paris" => {"champs elysee" => [...]},
         "lyon" => {"lyon street1" => [...]}
      }
    ]
  },
  {
    "italy" => [
      {
         "rome" => {"rome street 1" => [...]},
         ...
      }
    ]
  },
]

能够改变我的等级

我尝试了很多东西,但我找不到任何解决方案......我认为没有正确的方法。

编辑:

这是我的代码(根本不工作),但这是主意。

require 'json'

@hierarchie = ["country", "city", "street"]

f = File.read("./data.json")
@ret = JSON.parse(f)

@tree = []
def render_as_tree
    @ret.each do |datum|
         ptr = @tree
         @hierarchie.each do |h|
             if ptr.empty?
                 ptr << {datum[h] => []}
             end
             obj = ptr.detect {|d| datum[h]}
             ptr = obj[ datum[h] ]
         end
    end
    return @tree
end

puts render_as_tree()

此代码是我的代码的一个小例子。我的代码不是国家/城市/街道,但它更适合理解和解释。

...表示孩子的数量可能会动态变化。对于这个例子,我可以country/city/district/street/house/...country/city,所以我的最后数据需要是平面数据。

4 个答案:

答案 0 :(得分:1)

您可以使用injecteach_with_object来浏览数据

data = [
  {"country": "france", "city": "paris", "street": "champs elysee"},
  {"country": "france", "city": "Lyon", "street": "lyon street"},
  {"country": "italy", "city": "rome", "street": "rome street1"},
]

hierarchy = data.each_with_object({}) do |row, hsh|
  country = row[:country]
  city = row[:city]
  street = row[:street]
  hsh[country] ||= {}
  hsh[country][city] ||= {}
  hsh[country][city][street] ||= []
  hsh[country][city][street] << "?" # put here anything you need
end

p hierarchy
#=> {"france"=>{"paris"=>{"champs elysee"=>["?"]}, "Lyon"=>{"lyon street"=>["?"]}}, "italy"=>{"rome"=>{"rome street1"=>["?"]}}}

UPD:

def tree(data, hierarchy)
  data.each_with_object({}) do |row, hsh|
    node = hsh
    hierarchy.each do |level|
      val = row[level]
      node[val] ||= {}
      node = node[val]
    end
  end
end

tree(data, [:country, :city])
#=> {"france"=>{"paris"=>{}, "Lyon"=>{}}, "italy"=>{"rome"=>{}}}
tree(data, [:country, :city, :street])
#=> {"france"=>{"paris"=>{"champs elysee"=>{}}, "Lyon"=>{"lyon street"=>{}}}, "italy"=>{"rome"=>{"rome street1"=>{}}}}

答案 1 :(得分:0)

arr = [{"country": "france", "city": "paris", "street": "champs elysee"},
       {"country": "france", "city": "lyon",  "street": "rue mandelot"},
       {"country": "italy", "city": "rome", "street": "via del corso"},
       {"country": "france", "city": "paris", "street": "rue montorgueil"}]

arr.each_with_object({}) {|g,h| ((h[g[:country]] ||={})[g[:city]] ||={})[g[:street]]=['x']}
  #=> {"france"=>{"paris"=>{"champs elysee"=>["x"], "rue montorgueil"=>["x"]},
  #               "lyon"=>{"rue mandelot"=>["x"]}},
  #    "italy"=>{"rome"=>{"via del corso"=>["x"]}}}

@ fl00r我都返回了一个由嵌套哈希组成的对象,而不是问题中指定的交替哈希和数组。由于每个数组都包含一个哈希值,因此它们没有用处。

答案 2 :(得分:0)

感谢@ProGM,我找到了解决方案!

The specification

class Tree
  attr_reader :data

  def initialize(data)
    @data = data
  end

  def +(other)
    Tree.new(deep_merge(@data, other.is_a?(Tree) ? other.data : other))
  end

  delegate :empty?, to: :data

  def replace(other)
    @data.replace(other.data)
  end

  def root
    level(0)
  end

  def levels
    output = []
    i = -1
    while i += 1
      current_level = level(i)
      break if current_level.all?(&:nil?)
      output << current_level
    end
    output
  end

  def level(level_number)
    all_elements_at_level(@data, level_number)
  end

  private

  def deep_merge(a, b)
    case a
    when Hash
      return merge_hashes(a, b) if b.is_a?(Hash)
      return merge_array_hash(b, a) if b.is_a?(Array)
      [b, a]
    when Array
      return merge_arrays(a, b) if b.is_a?(Array)
      return merge_array_hash(a, b) if b.is_a?(Hash)
      [b] + a
    else
      return [a, b] if b.is_a?(Hash)
      return [a] + b if b.is_a?(Array)
      a == b ? a : [a, b]
    end
  end

  def merge_array_hash(a, b)
    if a.last.is_a? Hash
      a[0...-1] + [merge_hashes(a.last, b)]
    else
      a + [b]
    end
  end

  def merge_hashes(a, b)
    a.deep_merge(b) do |_, this_val, other_val|
      deep_merge(this_val, other_val)
    end
  end

  def merge_arrays(a, b)
    keys = merge_array_keys(a, b)
    hashes = merge_hashes(a.last.is_a?(Hash) ? a.last : {}, b.last.is_a?(Hash) ? b.last : {})
    if hashes.empty?
      keys
    else
      (keys - hashes.keys) + [hashes]
    end
  end

  def merge_array_keys(a, b)
    (a.reject { |e| e.is_a?(Hash) } + b.reject { |e| e.is_a?(Hash) }).uniq
  end

  def all_elements_at_level(data, level_number)
    return ground_level(data) if level_number == 0
    case data
    when Hash
      data.map { |_, v| all_elements_at_level(v, level_number - 1) }
    when Array
      data.map { |e| all_elements_at_level(e, level_number) }.flatten
    end
  end

  def ground_level(data)
    case data
    when Hash
      data.keys
    when Array
      data.map { |e| all_elements_at_level(e, 0) }.flatten
    else
      data
    end
  end
end

答案 3 :(得分:-1)

require "json"

data = %|[{"country": "france", "city": "paris", "street": "champs elysee"},{"country": "france", "city": "Lyon", "street": "lyon street"},{"country": "italy", "city": "rome", "street": "rome street1"}]|

parsed = JSON.parse(data)

final = {}

parsed.each {
    |h|

    if final.has_key? h["country"]
        if final[h["country"]].key? h["city"]
            final[h["country"]][h["city"]].push h["street"]
        else
            final[h["country"]][h["city"]] = [ h["street"]]
        end
    else
        final[h["country"]] = {h["city"] => [ h["street"] ]}
    end
}

p final