Search code examples
rubyhashfilterhash-of-hashes

Filter hash of hashes array to get only certain key & values


Using psd.rb gem to return a hash of the properties of a psd file. However I would like to somehow filter the results down just to the font name. Is there a method that can get the value of keys nested within a certain hash e.g the :font hash.

Thanks


Solution

  • require 'json'
    
    # h = <paste object from your gist here, and remove any characters that cause ruby to error>
                 
    def key_occurences(obj, k)
    
      # deal with arrays recursively
      return obj.map {|x| key_occurences(x,k) } if obj.is_a?(Array)
    
      # otherwise assume we have a Hash
      obj.to_a.map do |x|
        return x[1] if x[0] == k
        return key_occurences(x[1], k) if x[1].is_a?(Array) || x[1].is_a?(Hash) #recurse again if our key is itself a Hash or Array
      end.compact
    end
    
    puts key_occurences(h, :font).to_json
    

    Your output will be:

    [[{"name":"Georgia","sizes":[16.0],"colors":[[255,102,102,102]],"css":"font-family: \"Georgia\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 16.0pt;\ncolor: rgba(102, 102, 102, 255);"},{"name":"AntennaCond-Black","sizes":[16.0],"colors":[[255,89,89,87]],"css":"font-family: \"AntennaCond-Black\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 16.0pt;\ncolor: rgba(89, 89, 87, 255);"},[],[],[],[],[],[],{"name":"ArialMT","sizes":[15.0],"colors":[[255,51,51,51]],"css":"font-family: \"ArialMT\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 15.0pt;\ncolor: rgba(51, 51, 51, 255);"},{"name":"Georgia","sizes":[19.0],"colors":[[255,167,148,72]],"css":"font-family: \"Georgia\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 19.0pt;\ncolor: rgba(167, 148, 72, 255);"},[],[],[]],[[{"name":"MyriadPro-Regular","sizes":[65.0],"colors":[[255,255,255,255]],"css":"font-family: \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 65.0pt;\ncolor: rgba(255, 255, 255, 255);"},{"name":"ArialMT","sizes":[14.0],"colors":[[255,255,255,255]],"css":"font-family: \"ArialMT\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 14.0pt;\ncolor: rgba(255, 255, 255, 255);"},{"name":"Georgia","sizes":[18.0],"colors":[[255,255,255,255]],"css":"font-family: \"Georgia\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 18.0pt;\ncolor: rgba(255, 255, 255, 255);"},[],[],[],[],[],[],[],[],{"name":"AntennaCond-Black","sizes":[18.0,25.0],"colors":[[255,89,89,87]],"css":"font-family: \"AntennaCond-Black\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 18.0pt;\ncolor: rgba(89, 89, 87, 255);"},{"name":"AntennaCond-Black","sizes":[28.0],"colors":[[255,229,42,49]],"css":"font-family: \"AntennaCond-Black\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 28.0pt;\ncolor: rgba(229, 42, 49, 255);"},{"name":"AntennaCond-Black","sizes":[28.0],"colors":[[255,255,255,255]],"css":"font-family: \"AntennaCond-Black\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 28.0pt;\ncolor: rgba(255, 255, 255, 255);"},[],{"name":"AntennaCond-Black","sizes":[28.0],"colors":[[255,255,255,255]],"css":"font-family: \"AntennaCond-Black\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 28.0pt;\ncolor: rgba(255, 255, 255, 255);"},{"name":"AntennaCond-Black","sizes":[15.0],"colors":[[255,229,42,49]],"css":"font-family: \"AntennaCond-Black\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 15.0pt;\ncolor: rgba(229, 42, 49, 255);"},[],{"name":"AntennaCond-Black","sizes":[24.0,39.0],"colors":[[255,255,255,255]],"css":"font-family: \"AntennaCond-Black\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 24.0pt;\ncolor: rgba(255, 255, 255, 255);"},[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],{"name":"Georgia","sizes":[18.0,16.0],"colors":[[255,165,146,70],[255,102,102,102]],"css":"font-family: \"Georgia\", \"Georgia-Italic\", \"MyriadPro-Regular\", \"AdobeInvisFont\";\nfont-size: 18.0pt;\ncolor: rgba(165, 146, 70, 255);"},[],[],[],[],[],[]]],[]]
    

    And you can now further process that for the specific font info you want

    EDIT:

    Per our comments dicussion, try:

    def unwrap(arr)
      return arr if arr[0].is_a?(Hash)
      return nil if arr.is_a?(Array) && arr.size == 0
      unwrap(arr[0])
    end
    
    answer = key_occurences(h, :font).map! do |x| 
      unwrap(x)
    end.compact!.flatten!
    
    puts answer.map {|x| x[:name]}.uniq
    

    which outputs:

    Georgia
    AntennaCond-Black
    ArialMT
    MyriadPro-Regular