Friday, 18 November 2016

json - Prevent Nil Values in Transformed Array



Removing nils is quite simple, however, I'd like to know:




1) What am I doing wrong, why does my array result below include nils?



2) How I can PREVENT the nils from being added to my array, instead of removing them after the fact.



@cars = Array.new
plucked_array = [
[8, "Chevy", "Camaro", 20],
[9, "Ford", "Mustang", 55],
[9, "Ford", "Fusion", 150]

]
plucked_array.
each { |id, make, model, model_count|
@cars[id] ||= {name: make, id: make, data: []}
@cars[id][:data].push([model, model_count])
}
puts @cars.inspect
#=>[nil, nil, nil, nil, nil, nil, nil, nil, {:name=>"Chevy", :id=>"Chevy", :data=>[["Camaro", 20]]}, {:name=>"Ford", :id=>"Ford", :data=>[["Mustang", 55], ["Fusion", 150]]}]

puts @cars.compact.inspect

#=>[{:name=>"Chevy", :id=>"Chevy", :data=>[["Camaro", 20]]}, {:name=>"Ford", :id=>"Ford", :data=>[["Mustang", 55], ["Fusion", 150]]}]
# This gives the result I'm looking for,
# just wondering how to best get transformed array without the post-cleanup.


I also attempted @theTinMan's recommendation to select first, then map, but I had the same results:



plucked_array.select { |id, make, model, model_count|
@cars[id] = {'name' => make, 'id' => make, 'data' => []}
}.map { |id, make, model, model_count|

@cars[id]['data'].push([model, model_count])
}
puts @cars.inspect
#=>[nil, nil, nil, nil, nil, nil, nil, nil, {:name=>"Chevy", :id=>"Chevy", :data=>[["Camaro", 20]]}, {:name=>"Ford", :id=>"Ford", :data=>[["Mustang", 55], ["Fusion", 150]]}]


I've tried, with partial success, to use Hash instead of an array for @cars. This prevented nils, but my end goal is to build "drilldown: series[]" below, which is an array of hashes:






// Create the chart
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Imaginary Car Stats'
},
subtitle: {
text: 'Click the columns to view models.'

},
xAxis: {
type: 'category'
},
yAxis: {
title: {
text: 'Total car count'
}
},
legend: {

enabled: false
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true,
format: '{point.y}'
}
}

},
tooltip: {
headerFormat: '{series.name}
',
pointFormat: '{point.name}: {point.y:.2f}% of total
'
},
/*I have separate `pluck` query for this top-level series:*/
"series": [
{
"name": "Cars",
"colorByPoint": true,

"data": [
{
"name": "Ford",
"y": 205,
"drilldown": "Ford"
},
{
"name": "Chevy",
"y": 20,
"drilldown": "Chevy"

},
{
"name": "Other",
"y": 16,
"drilldown": null
}
]
}
],
"drilldown": {

/*This is the array of hashes I'm attempting to create:*/
"series": [
{
"name": "Ford",
"id": "Ford",
"data": [
[
"Fusion",
150
],

[
"Mustang",
55
]
]
},
{
"name": "Chevy",
"id": "Chevy",
"data": [

[
"Camaro",
20
]
]
}
]
}
});










Answer



The reason your code is behaving the way it does is because array[8] = x inserts x at position 8 in the array, and ruby fills the spaces up to 8 with nils.




a = []
a[7] = 4
a == [nil, nil, nil, nil, nil, nil, nil, 4]


You need @cars to be a hash - not an array



I think this will do what you are trying to do:



plucked_array = [

[8, "Chevy", "Camaro", 20],
[9, "Ford", "Mustang", 55],
[9, "Ford", "Fusion", 150]
]

cars = plucked_array.each_with_object({}) do |(id, make, model, count), cars|
cars[id] ||= {id: id, make: make, data: []}
cars[id][:data] << [model, count]
end


p cars.values


Which actually is almost identical to @Austio's solution.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...