0

I'm trying to use Highcharts in my web app, but I'm having some trouble getting my arrays to match the output that highcharts needs, essentially hightcharts needs output like this in it's JS:

series: [{
   name: 'person',
   data: [1562, 873, 1457]
}, {
   name: 'car',
   data: [7323, 324, 1233]
}, {
   name: 'SUV',
   data: [832, 6232, 4432]
}]

Where each item in the data array in this case is a new day, my problem is that my data is organized a bit differently, and trying to transform my data to match this has been a headache. basically my output is like so:

[
  {:date=>"Sun, 10 Apr 2011", :object_types=>[
    {:count=>4279, :class_name=>"person"}, 
    {:count=>8785, :class_name=>"car"}, 
    {:count=>2153, :class_name=>"SUV"}
  ]}, 
  {:date=>"Sat, 09 Apr 2011", :object_types=>[
    {:count=>12206, :class_name=>"person"}, 
    {:count=>29095, :class_name=>"car"}, 
    {:count=>7565, :class_name=>"SUV"}
  ]}, 
  {:date=>"Fri, 08 Apr 2011", :object_types=>[
    {:count=>4159, :class_name=>"person"}, 
    {:count=>8043, :class_name=>"car"}, 
    {:count=>1982, :class_name=>"SUV"}
  ]}
] 

So it seems that I need to primarily have the items first grouped by (in my case) :class_name, then within that the :count by day as each item in data.

I'm having trouble find the the "ruby way" to do this elegantly, I'm not super awesome at manipulating arrays and hashes as you can probably see, but any bit of help in guiding me towards accomplishing this the right way is appreciated.


Secondly, I tried to accomplish this using group() in my query originally but that doesnt seem to be helping much, thoughts? Another thing I'll have to solve is filling dates with "0" if there are no records for it... but I'll tackle that next I think.

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
JP Silvashy
  • 46,977
  • 48
  • 149
  • 227

3 Answers3

1

Something like this should work:

def convert_data(input) 
  hash = {} 
  input.each do |o| 
    o[:object_types].each do |ot| 
      if hash[ot[:class_name]] 
        hash[ot[:class_name]] << ot[:count] 
      else 
        hash[ot[:class_name]] = [ot[:count]] 
      end 
    end 
  end 
  hash.map {|k,v| {'name' => k, 'data' => v}}
end

You can call this function with your input data and you've got an object which fits the format you expect for the Highcharts library.

maerics
  • 151,642
  • 46
  • 269
  • 291
1
aux = Hash.new(Array.new)
series = Array.new

# After this code the aux hash will look something like this: {:person => [4159, 1231, 255], :suv => [1231, 4123, 5411], :car => [321, 312, 541]}
raw_array.each do |date_hash|
  date_hash[:object_types].each do |inf|
    aux[inf[:class_name]] << inf[:count]
  end
end

aux.each { |k,v| series << {:name => k.to_s, :data => v} }

Then that series array is what you need. Just render it like json.

pedrofs
  • 612
  • 1
  • 6
  • 14
  • You probably mean `Hash.new{|h, k| h[k] = []}`, not `Hash.new(Array.new)` – Marc-André Lafortune Apr 10 '11 at 23:47
  • Nope. `Hash.new([])`, `Hash.new{[]}` and `Hash.new{|h, k| h[k] = []}` are different. See http://stackoverflow.com/questions/2692141/hash-default-value-not-being-used & http://stackoverflow.com/questions/2552579/ruby-method-array-not-updating-the-array-in-hash – Marc-André Lafortune Jun 18 '11 at 17:48
1

I would use Enumerable#group_by, because that's the harder part; the rest is just basic transformation:

r = data.flat_map{|h| h[:object_types]}.
  group_by{|h| h[:class_name]}.
  map do |class_name, hashes|
    {:name => class_name, :data => hashes.map{|h| h[:count]}}
  end

Note: group_by is in ActiveRecord or Ruby 1.8.7+. flat_map is new to Ruby 1.9.2. Use the right version, or require "backports".

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166