防止转换后的数组中出现 Nil 值
Prevent Nil Values in Transformed Array
删除nils
是quite simple,但是,我想知道:
1) 我做错了什么,为什么我下面的数组结果包含 nils
?
2) 如何防止 nils
添加到我的数组中,而不是事后删除它们。
@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.
我也尝试过@theTinMan 的 recommendation 先 select
,然后 map
,但我得到了相同的结果:
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]]}]
我曾尝试使用哈希代替 @cars
的数组,但取得了部分成功。这防止了 nils,但我的最终目标是在下面构建 "drilldown: series[]",这是一个哈希数组:
// 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: '<span style="font-size:11px">{series.name}</span><br>',
pointFormat: '<span style="color:{point.color}">{point.name}</span>: <b>{point.y:.2f}%</b> of total<br/>'
},
/*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
]
]
}
]
}
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/data.js"></script>
<script src="https://code.highcharts.com/modules/drilldown.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
我会选择更多的 reduce 方法,因为您正在获取一个列表并将其归结为另一个对象。 each_with_object
执行 reduce 但隐含地 returns 每个循环中的 obj(在本例中为汽车)
new_list = plucked_array.each_with_object({}) do |(id, make, model, model_count), cars|
# return before mutating cars hash if the car info is invalid
cars[id] ||= {name: make, id: make, data: []}
cars[id][:data].push([model, model_count])
end
# Then in your controller to handle the usage as an array
@cars = new_list.values
旁注:地图通常更多地用于转换或等效更改,这就是我认为这里感觉不对的原因。
我建议按 id 分组,然后创建 hash
:
plucked_array.group_by(&:first).transform_values{ |v| v.map{ |id, make, model, model_count| {name: make, id: make, data: [model, model_count]} }}
# {8=>[{:name=>"Chevy", :id=>"Chevy", :data=>["Camaro ls", 20]}],
# 9=>[{:name=>"Ford", :id=>"Ford", :data=>["Mustang", 55]}, {:name=>"Ford", :id=>"Ford", :data=>["Fusion", 150]}]}
# }
编辑 - 更新确实匹配原始问题的编辑(获取 Highcharts 的系列数据)
这应该return要求的结果:
plucked_array.map.with_object(Hash.new([])) { |(id, make, model, model_count), h|
h[make] += [[model, model_count]] }.map { |k, v| {name: k, id: k, data: v }}
#=> [{:name=>"Chevy", :id=>"Chevy", :data=>[["Camaro", 20]]}, {:name=>"Ford", :id=>"Ford", :data=>[["Mustang", 55], ["Fusion", 150]]}]
第一部分构建这样的哈希。
#=> {"Chevy"=>[["Camaro", 20]], "Ford"=>[["Mustang", 55], ["Fusion", 150]]}
将 Hash.new([])
作为对象传递允许将元素插入默认数组。
最终哈希映射到所需的键。
您的代码之所以如此,是因为 array[8] = x
在数组的第 8 位插入 x
,而 ruby 用 nils 填充最多 8 个空格。
a = []
a[7] = 4
a == [nil, nil, nil, nil, nil, nil, nil, 4]
您需要 @cars
才能成为哈希 - 而不是数组
我认为这会完成您想要做的事情:
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
这实际上与@Austio 的解决方案几乎相同。
删除nils
是quite simple,但是,我想知道:
1) 我做错了什么,为什么我下面的数组结果包含 nils
?
2) 如何防止 nils
添加到我的数组中,而不是事后删除它们。
@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.
我也尝试过@theTinMan 的 recommendation 先 select
,然后 map
,但我得到了相同的结果:
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]]}]
我曾尝试使用哈希代替 @cars
的数组,但取得了部分成功。这防止了 nils,但我的最终目标是在下面构建 "drilldown: series[]",这是一个哈希数组:
// 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: '<span style="font-size:11px">{series.name}</span><br>',
pointFormat: '<span style="color:{point.color}">{point.name}</span>: <b>{point.y:.2f}%</b> of total<br/>'
},
/*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
]
]
}
]
}
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/data.js"></script>
<script src="https://code.highcharts.com/modules/drilldown.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
我会选择更多的 reduce 方法,因为您正在获取一个列表并将其归结为另一个对象。 each_with_object
执行 reduce 但隐含地 returns 每个循环中的 obj(在本例中为汽车)
new_list = plucked_array.each_with_object({}) do |(id, make, model, model_count), cars|
# return before mutating cars hash if the car info is invalid
cars[id] ||= {name: make, id: make, data: []}
cars[id][:data].push([model, model_count])
end
# Then in your controller to handle the usage as an array
@cars = new_list.values
旁注:地图通常更多地用于转换或等效更改,这就是我认为这里感觉不对的原因。
我建议按 id 分组,然后创建 hash
:
plucked_array.group_by(&:first).transform_values{ |v| v.map{ |id, make, model, model_count| {name: make, id: make, data: [model, model_count]} }}
# {8=>[{:name=>"Chevy", :id=>"Chevy", :data=>["Camaro ls", 20]}],
# 9=>[{:name=>"Ford", :id=>"Ford", :data=>["Mustang", 55]}, {:name=>"Ford", :id=>"Ford", :data=>["Fusion", 150]}]}
# }
编辑 - 更新确实匹配原始问题的编辑(获取 Highcharts 的系列数据)
这应该return要求的结果:
plucked_array.map.with_object(Hash.new([])) { |(id, make, model, model_count), h|
h[make] += [[model, model_count]] }.map { |k, v| {name: k, id: k, data: v }}
#=> [{:name=>"Chevy", :id=>"Chevy", :data=>[["Camaro", 20]]}, {:name=>"Ford", :id=>"Ford", :data=>[["Mustang", 55], ["Fusion", 150]]}]
第一部分构建这样的哈希。
#=> {"Chevy"=>[["Camaro", 20]], "Ford"=>[["Mustang", 55], ["Fusion", 150]]}
将 Hash.new([])
作为对象传递允许将元素插入默认数组。
最终哈希映射到所需的键。
您的代码之所以如此,是因为 array[8] = x
在数组的第 8 位插入 x
,而 ruby 用 nils 填充最多 8 个空格。
a = []
a[7] = 4
a == [nil, nil, nil, nil, nil, nil, nil, 4]
您需要 @cars
才能成为哈希 - 而不是数组
我认为这会完成您想要做的事情:
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
这实际上与@Austio 的解决方案几乎相同。