将元素从一个数组传递到另一个数组,生成CSV

时间:2018-06-12 17:36:42

标签: arrays ruby csv sorting each

我试图在Ruby中编写一个程序,允许一个数组从另一个数组接收信息。基本上,我有一个名为" student_array"的多维数组。其中包含少数学生的信息

student_array = [["Mike", 13, "American", "male"], 
["Grace", 12, "Canadian", "female"],
["Joey", 13, "American", "male"],
["Lily", 13, "American", "female"]
]

我还初始化了另外两个将统计国籍的阵列:

nationality_array = Array.new
nationality_count = Array.new

该计划的目的是循环学生数组,计算学生的不同国籍,并创建一个CSV文件,其中包含不同国籍的标题,以及每个国家的计数。

预期output.csv

American, Canadian
3, 1

这是我到目前为止的代码

student_array.each do |student|
    #pushes the nationality string into the nationality array
    nationality_array.push(student[2])   
end

所以nationality_array目前应该是这样的:

nationality_array = ["American", "Canadian", "American", "American"];
nationality_array.uniq = ["American", "Canadian"];

所以我会有两个标题 - " American"和#34;加拿大"

现在我需要一种方法来遍历student_array,计算" American"的每个实例。和"加拿大",并以某种方式将其分配回国籍阵列。我很难想象如何解决这个问题。这是我到目前为止 -

american_count = 0;
canadian_count = 0;

student_array.each do |student|
    if student[2] = "American"
        american_count++
    elsif student[2] = "Canadian"
        canadian_count++
    end
end

nationality_count.push(american_count);
nationality_count.push(canadian_count);

好的,现在我在nationality_count数组中有这些计数,但是如何将它传递给CSV,确保它们被分配到正确的标题?我觉得我的代码非常笨拙,而且可以更精简。

它可能看起来像这样?

CSV.open("output/redemptions.csv", "wb") do |csv|
    csv << [nationality_array]
    csv << [nationality_count]
end

任何人都可以提供更清晰的方法吗?

3 个答案:

答案 0 :(得分:3)

Ruby核心中的

Array#group_by和ActiveSupport中的Hash#transform_values是两种非常有用的方法,可以在这里使用:

require 'active_support/all'
require 'csv'

student_array = [
  ["Mike", 13, "American", "male"], 
  ["Grace", 12, "Canadian", "female"],
  ["Joey", 13, "American", "male"],
  ["Lily", 13, "American", "female"]
]

counts = student_array.group_by { |attrs| attrs[2] }.transform_values(&:length)
# => => {"American"=>3, "Canadian"=>1}

CSV.open("output/redemptions.csv", "wb") do |csv|
    csv << counts.keys
    csv << counts.values
end

puts File.read "output/redemptions.csv"
# => American,Canadian
#    3,1

.group_by { |attrs| attrs[2] }将数组转换为哈希值,其中键是attrs[2]的唯一值,值是具有attrs[2]的元素列表。此时,您可以使用transform_values将这些值转换为表示其长度的数字(表示具有该特定attrs[2]的元素数量)。然后可以从散列中提取键和值作为单独的数组。

答案 1 :(得分:3)

您可以使用Hash按国籍而不是不同的数组对计数进行分组。

nationalities_count = student_array.each_with_object(Hash.new(0)) do |student, hash| 
  nationality = student[2]
  hash[nationality] += 1
end

这会给你一个看起来像

的哈希
{ "American" => 2, "Canadian" => 1 }

然后您可以使用Hash#to_aArray#transpose,如下所示:

hsh = { "American" => 2, "Canadian" => 1 }
 => {"American"=>2, "Canadian"=>1}
2.4.2 :002 > hsh.to_a
 => [["American", 2], ["Canadian", 1]]
2.4.2 :003 > hsh.to_a.transpose
 => [["American", "Canadian"], [2, 1]]

最后,要输出CSV文件,您只需将数组写入文件

即可
nationalities_with_count = hash.to_a.transpose
CSV.open("output/redemptions.csv", "wb") do |csv|
  csv << nationalities_with_count[0]
  csv << nationalities_with_count[1]
end

答案 2 :(得分:2)

您甚至不需要CSV工具:

result =
  student_array.
     map { |a| a[2] }.            # get nationalities
     group_by { |e| e }.          # hash
     map { |n, c| [n, c.count] }. # map values to count
     transpose.                   # put data in rows
     map { |row| row.join ',' }.  # join values in a row
     join($/)                     # join rows
#⇒ American,Canadian
#  3,1

现在你有一个有效的CSV字符串,只需将其吐出到文件中。