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