Ruby CSV::表排序到位



我正在对CSV::Table对象进行排序。我有一个包含标题("date""amount""source")和O(50)条目的表。

输入:

data = CSV.table('filename.csv', headers:true) # note headers are :date, :source, :amount
amounts = []
data[:amount].each {|i| amounts << i.to_f}
data.sort_by! {|row| row[:amount]} 
# error - not a defined function
data = data.sort_by {|row| row[:amount]} 
# sorted but data is now an array not CSV::Table. would like to retain access to headers

我想要一个bang函数,在不丢失CSV::Table结构的情况下,按"amount"列对表进行适当排序。具体来说,我希望结果是CSV::表,这样我仍然可以访问标题。现在,我得到了一个数组,这不是我想要的。

我相信有一种更简单的方法可以做到这一点,尤其是使用CSV::Table类。有什么帮助吗?

您可以使用:

CSV::Table.new(data)将Array转换为CSV::Table对象(如果需要的话)。

sort_by是来自Enumerable模块的一个方法,当块作为参数时,它将始终返回一个数组

假设您定义以下字符串:

txt=<<-CSV_TXT
Item, Type, Amount, Date
gasoline, expense, 200.00, 2022-01-01
Food, expense, 25.66, 2021-12-24
Plates, expense, 333.03, 2021-04-24
Presents, expense, 1500.01, 2021-12-15
Pay check, income, 2000, 2021-12-07
Consulting, income, 300, 2021-12-16
CSV_TXT
# for giggles, using a multi character separator of ', '

现在从中创建一个CSV表(在IRB中…):

> require 'csv'
=> true
> options={:col_sep=>", ", :headers=>true, :return_headers=>true}
=> {:col_sep=>", ", :headers=>true, :return_headers=>true}
> data=CSV.parse(txt, **options)
=> #<CSV::Table mode:col_or_row row_count:7>

我们现在有一个定义了头的CSV::Table。如果使用CSV::Table,则标头不是可选的。

现在有两种方法(据我所知)可以根据Date字段对该表进行排序,最后得到一个标题不变的CSV::Table。两者都不是一个完全"到位"的解决方案。

第一,在往返一个CSV::Rows数组之后创建一个新的CSV::Table。对.sort_by的调用为您创建了CSV::Rows的数组,您可以为CSV::Row编制索引以进行排序。您使用现有表的第一行作为头参数:

> data=CSV::Table.new([data[0]]+data[1..].sort_by{ |r| r[3] })
=> #<CSV::Table mode:col_or_row row_count:7>

第二个类似,但通过使用.to_a创建数组,可以更容易地拆分标头。这也允许单独的行被过滤或以其他方式进一步处理:

> data=CSV.parse(txt, **options).to_a
=> 
[["Item", "Type", "Amount", "Date"],
...
> header=data.shift.to_csv(**options)
=> "Item, Type, Amount, Daten"

现在你有了data,去掉了头。有了这个数组,你可以随意排序、过滤或处理;然后放回CSV字符串的数组中。一切就绪:

> data.sort_by!{|r| r[3]}.map!{|r| r.to_csv(**options)}
=> 
["Plates, expense, 333.03, 2021-04-24n",
""Pay check", income, 2000, 2021-12-07n",
"Presents, expense, 1500.01, 2021-12-15n",
"Consulting, income, 300, 2021-12-16n",
"Food, expense, 25.66, 2021-12-24n",
"gasoline, expense, 200.00, 2022-01-01n"]

(注意带有"Pay check"的字段被引用。如果多字符:col_sep中的任何字符在字段中,Ruby将引用它…)

要打印第一个,只需使用puts data,因为Ruby知道如何打印CSV::Table;要打印第二个,您可以执行puts header,data.join("")

对于第二个,要将标头和数据重新加入到一个新表中,请再次使用parseoptions

> data_new=CSV.parse(header+data.join(""), **options)
=> #<CSV::Table mode:col_or_row row_count:7>

最新更新