Merge Hashes and key gets both, old and new values

2019-05-11 08:25发布

Imagine I have an array like this

array = [4,"hello",[[3],:d,7,[:a,"seven"]]]



class Array
  def deep_collect_by_elem_type_as_hash()
    e = {}
    self.each(){|x|
      if x.is_a?(Array)
        e.merge(x.deep_collect_by_elem_type_as_hash)
      end
      if !x.is_a?(Array)
        if e.has_key?(x.class)
          e[x.class]<<x
        else
          e[x.class] = [x]
        end
      end
    }
    return e
  end

I want all my arrays to create an hash in which there are keys containing the different classes that are in my Array. Their values will be the actual elements for each class.

So it would look like this:

{Fixnum=>[4, 3, 7], String=>["hello", "seven"], Symbol=>[:d, :a]}

I want to solve the whole thing without using flatten, but doing it recrusive. Te flatten solution could look like this:

def deep_collect_by_elem_type_as_hash1()
    e = {}
    flat= self.flatten()
    flat.each(){|x|
      if e.has_key?(x.class)
        e[x.class]<<x
      else
        e[x.class] = [x]
      end
    }
    return e
  end

For those wondering why I do not want to use flatten: I still have problems fully understanding how to implement recursive methods, and therefore this is a question to give me a better understanding.

I think I somehow have to implement merge with a block, but I cannot figure out a proper block. merge(x.deep_collect_by_elem_type_as_hash(){|k,v1,v2| help}

2条回答
我只想做你的唯一
2楼-- · 2019-05-11 09:08

You can do that thus:

def group_by_class(arr, h = Hash.new { |h,k| h[k] = [] })
  arr.each { |e| e.is_a?(Array) ? group_by_class(e,h) : h[e.class] << e }
  h
end

array = [4,"hello",[[3],:d,7,[:a,"seven"]]]
group_by_class(array)
  #=> {Fixnum=>[4, 3, 7], String=>["hello", "seven"], Symbol=>[:d, :a]}

array = [4,"hello",[[3],:d,7,[:a,"seven",["b",9,[:e,["cat", {a: 0}, 5]]]]]]
group_by_class(array)
  #=> {Fixnum=>[4, 3, 7, 9, 5], String=>["hello", "seven", "b", "cat"],
  #    Symbol=>[:d, :a, :e], Hash=>[{:a=>0}]} 

You could also write:

def group_by_class(arr)
  arr.each { |e| e.is_a?(Array) ? group_by_class(e) : @h[e.class] << e }
end

@h = Hash.new { |h,k| h[k] = [] }
group_by_class(array)
@h
  #=> {Fixnum=>[4, 3, 7, 9, 5], String=>["hello", "seven", "b", "cat"],
  #    Symbol=>[:d, :a, :e], Hash=>[{:a=>0}]} 
查看更多
男人必须洒脱
3楼-- · 2019-05-11 09:10

here we go:

a = [4,"hello",[[3],:d,7,[:a,"seven"]]]

def stuff(a)
  res = {}
  a.each do |e|
    if e.is_a?(Array)
      stuff(e).each do |k,v|
        res[k] ||= []
        v.each {|x| res[k] << x}
      end
    else
      k = e.class
      res[k] ||= []
      res[k] << e
    end
  end
  res
end

puts stuff(a).inspect

if you need to open and extend array, you can do something along the lines of:

class Array
  def stuff(a = self)
  ...
  end
end
查看更多
登录 后发表回答