在ruby中从csv文件中获取数据,并在desc中对数据进行排序?

xdnvmnnf  于 2021-09-29  发布在  Java
关注(0)|答案(2)|浏览(235)

这是我的csv文件值

Name , Sales
Randy, 200
Robin, 502
Randy, 200
Raj,   502
Randy, 500      
Robin, 102      
Mano,  220
Raj,   502
Randy, 285
Robin, 385
Randy, 295
Raj,   596

我需要这个输出

Name  Sales  Rank 
   Randy 1596   1
   Raj   1354   2
   Robin  988   3
   Mano   860   4

当我使用 max and most 它只显示最大值,最大值对我不起作用。我需要帮助请任何人帮我谢谢

cust = Hash.new

  CSV.foreach(("./test.csv"), headers: true, col_sep: ",") do |row|

    cust.store(row["Name"],row["Sales"])   
  end

  print cust

我目前正在尝试使用哈希,但它会保存最后的数据

bf1o4zei

bf1o4zei1#

如果要使用 row["COL"] csv语法。此外,您还需要使用 +== 把它们加起来。现在,您只是每次都覆盖该值。

Name,Sales
Randy, 200
Robin, 502
Randy, 200
Raj,   502
Randy, 500      
Robin, 102      
Mano,  220
Raj,   502
Randy, 285
Robin, 385
Randy, 295
Raj,   596
require 'csv'
cust = Hash.new

CSV.foreach(("./data.csv"), headers: true, col_sep: ",") do |row|
  # if key isn't in hash start it at 0
  unless cust.keys.include?(row["Name"])
    cust[row["Name"]] = 0 
  end
  # add the sales (.to_i converts to integer)
  cust[row["Name"]] += row["Sales"].to_i
end

puts "Name Sales Rank"

# Sort by the value not the key. Make it negative so it's descending

# not ascending.

# each_with_index is just a nice way to count them

cust.sort_by{|k,v| -v}.each_with_index do |(name, sales), i|
  puts "#{name} #{sales} #{i+1}"
end

生产:

Raj 1600 1
Randy 1480 2
Robin 989 3
Mano 220 4
sgtfey8w

sgtfey8w2#

首先,让我们创建一个包含该信息的文件。

str =<<~_
Name , Sales
Randy, 200
Robin, 502
Randy, 200
Raj,   502
Randy, 500      
Robin, 102      
Mano,  220
Raj,   502
Randy, 285
Robin, 385
Randy, 295
Raj,   596
_
FNAME = 'f.csv'
File.write(FNAME, str)
  #=> 157

csv文件具有固定的列分隔符(字符串)。按照惯例,分隔符将是逗号,在这种情况下,该文件的内容将显示如下:

Name,Sales
Randy,200
Robin,502
Randy,200
Raj,502
Randy,500      
Robin,102      
Mano,220
Raj,502
Randy,285
Robin,385
Randy,295
Raj,596

或者,也可以使用分离器 ", " ,但这不是所有行的分隔符。第一行在逗号前包含一个空格,其他一些行在逗号后包含两个或更多空格。因此,您应该使用逗号(默认值)作为列分隔符,然后在需要时,去掉开头和结尾的空格。
我们可以先打开文件来创建一个 CSV 例如。

require 'csv'
csv = CSV.open(FNAME, headers: true)
  #=> #<CSV io_type:File io_path:"f.csv" encoding:UTF-8 lineno:0 col_sep:",
        " row_sep:"\n" quote_char:"\"" headers:true>

我们会发现 csv.class #=> CSVcsv.headers #=> true . 后者只是确认我们已经规定文件有头。由于尚未读取文件中的任何内容,因此不会返回头本身。读取标题后的第一行后 csv.headers 将返回一个标题数组。你不需要 col_sep: "," 因为默认的列分隔符是逗号。
现在我们读取文件并计算感兴趣的散列。

h = csv.each_with_object(Hash.new(0)) do |csv_row, h|
  name, sales = csv.headers
  h[csv_row[name].strip] += csv_row[sales].to_i
end
  #=> {"Randy"=>1480, "Robin"=>989, "Raj"=>1600, "Mano"=>220}

作为 CSV.included_modules.include?(Enumerable) #=> true ,可枚举#每个带有_对象的_枚举 csv ,它们是csv_行的示例(块变量的值 csv_row )并以hash::new方法的形式创建一个计数哈希,该方法接受一个参数(默认值)而不接受任何块。请注意,在计算中 name #=> "Name "sales #=> " Sales" .
我们现在可以检索标题:

name, sales = csv.headers.map(&:strip)
  #=> ["Name", "Sales"]

最好关闭该文件:

csv.close

虽然方法 CSV#close 没有记录。
我们现在可以操纵 hheaders 符合要求。例如,我们可以计算以下各项:

names, all_sales = h.sort_by { |name, sales| -sales }
                    .map { |name,sales| [name, sales.to_s] }
                    .transpose
  #=> [["Raj", "Randy", "Robin", "Mano"], ["1600", "1480", "989", "220"]]
max_name_len  = [name.size, names.max_by(&:size).size].max
  #=> 5
max_sales_len = [sales.size, all_sales.max_by(&:size).size].max
  #=> 4
RANK_NAME = "Rank"
max_rank_len  = [RANK_NAME.size, names.size].max
  #=> 4

然后以良好的格式显示结果。

puts name.ljust(max_name_len) + ' ' + sales.rjust(max_sales_len) +
  ' ' + RANK_NAME.rjust(max_sales_len)
(0..names.size-1).each { |i| puts names[i].ljust(max_name_len) + ' ' +
  all_sales[i].rjust(max_sales_len) + ' ' + (i+1).to_s.rjust(max_rank_len) }

这将显示以下内容。

Name  Sales Rank
Raj    1600    1
Randy  1480    2
Robin   989    3
Mano    220    4

请参阅string#ljust和string#rjust。也可以使用字符串#%或内核#sprintf。 CSV 具有内置转换器(和标头转换器)。如果有人写道:

csv = CSV.open(FNAME, headers: true, converters: :integer)

文件正文中的所有值都将转换为整数。不幸的是,这将转换 name 字段转换为整数以及 sales 这不是我们想要的。但是,我们可以创建一个自定义转换器来仅进行转换 sales 将值转换为整数。这是这样做的。

proc = ->(s) { s.match(/ *\d+ */) ? s.to_i : s }
csv = CSV.open(FNAME, headers: true, converters: proc)

然后我们可能会改变 csv_row[sales].to_icsv_row[sales] :

h = csv.each_with_object(Hash.new(0)) do |csv_row, h|
  name, sales = csv.headers
  h[csv_row[name].strip] += csv_row[sales]
end
  #=> {"Randy"=>1480, "Robin"=>989, "Raj"=>1600, "Mano"=>220}

如果有多个自定义转换器,我们会写:

CSV.open(FNAME, headers: true, converters: [proc1, proc2,...])

哪里 proc1 , proc2 ,... 是实现自定义转换器的过程。如果只有一个自定义转换器,如这里所示,我们可以编写 converters: procconverters: [proc] .
最后,如果您对使用csv文件有点生疏,您可以简单地将该文件视为普通文件:

headers, *body = File.readlines(FNAME)
                     .map { |s| s.strip.split(/ *, */) }
  #=> [["Name", "Sales"],
  #    ["Randy", "200"], ["Robin", "502"], ["Randy", "200"],
  #    ["Raj", "502"], ["Randy", "500"], ["Robin", "102"],
  #    ["Mano", "220"], ["Raj", "502"], ["Randy", "285"],
  #    ["Robin", "385"], ["Randy", "295"], ["Raj", "596"]]

因此:

headers
  #=> ["Name", "Sales"]
body
  #=> [["Randy", "200"], ["Robin", "502"], ["Randy", "200"],
  #    ["Raj", "502"], ["Randy", "500"], ["Robin", "102"],
  #    ["Mano", "220"], ["Raj", "502"], ["Randy", "285"],
  #    ["Robin", "385"], ["Randy", "295"], ["Raj", "596"]]
h = body.each_with_object(Hash.new(0)) do |(name, sales),h|
  h[name] += sales.to_i
end
  #=> {"Randy"=>1480, "Robin"=>989, "Raj"=>1600, "Mano"=>220}

然后像以前一样继续。

相关问题