gpt4 book ai didi

ruby - Ruby 中的 CSV 迭代,并按列值分组以获取每组的最后一行

转载 作者:行者123 更新时间:2023-12-04 08:38:00 24 4
gpt4 key购买 nike

我有一个交易数据的csv,列如下:

ID,Name,Transaction Value,Running Total,  
5,mike,5,5,
5,mike,2,7,
20,bob,1,1,
20,bob,15,16,
1,jane,4,4,
etc...
我需要遍历每一行并对交易值做一些事情,当我到达每个 ID 的最后一行时做一些不同的事情。
我目前做这样的事情:
total = ""
id = ""
idHold = ""
totalHold = ""

CSV.foreach(csvFile) do |row|

totalHold = total
idHold = id

id = row[0]
value = row[2]
total = row[3]

if id != idHold
# do stuff with the totalHold here
end
end
但这有一个问题——它跳过了最后一行。此外,关于它的某些东西感觉不对。我觉得应该有更好的方法来检测“ID”的最后一行。
有没有办法对 id 进行分组,然后检测 id 组中的最后一项?
注意:所有 id 都在 csv 中组合在一起

最佳答案

让我们首先构建一个 CSV 文件。

str =<<~END
ID,Name,Transaction Value,Running Total
5,mike,5,5
5,mike,2,7
20,bob,1,1
20,bob,15,16
1,jane,4,4
END
CSVFile = 't.csv'
File.write(CSVFile, str)
#=> 107
我将首先创建一个接受两个参数的方法: CSV::row 的实例和一个 bool 值来指示 CSV 行是否是组的最后一个( true 如果是)。
def process_row(row, is_last)
puts "Do something with row #{row}"
puts "last row: #{is_last}"
end
该方法当然会被修改为执行需要为每一行执行的任何操作。
以下是处理文件的三种方法。三者都使用方法 CSV::foreach逐行读取文件。此方法使用两个参数调用,文件名和选项哈希 { header: true, converters: :numeric }这表明文件的第一行是标题行,并且表示数字的字符串将被转换为适当的数字对象。这里是 "ID" 的值, "Transaction Value""Running Total"将被转换为整数。
虽然文档中没有提到,当 foreach在没有块的情况下调用它返回一个枚举器(与 IO::foreach 的方式相同)。
我们当然需要:
require 'csv'
链条 foreachEnumerable#chunk
我选择使用 chunk ,而不是 Enumerable#group_by , 因为文件的行已经按 ID 分组.
CSV.foreach(CSVFile, headers:true, converters: :numeric).
chunk { |row| row['ID'] }.
each do |_,(*arr, last_row)|
arr.each { |row| process_row(row, false) }
process_row(last_row, true)
end
显示
Do something with row 5,mike,5,5  
last row: false
Do something with row 5,mike,2,7
last row: true
Do something with row 20,bob,1,1
last row: false
Do something with row 20,bob,15,16
last row: true
Do something with row 1,jane,4,4
last row: true
注意
enum = CSV.foreach(CSVFile, headers:true, converters: :numeric).
chunk { |row| row['ID'] }.
each
#=> #<Enumerator: #<Enumerator::Generator:0x00007ffd1a831070>:each>
由这个枚举器生成的每个元素都被传递给块,块变量由一个名为 array decomposition 的进程赋值。 :
_,(*arr,last_row) = enum.next
#=> [5, [#<CSV::Row "ID":5 "Name":"mike" "Transaction Value":5 "Running Total ":5>,
# #<CSV::Row "ID":5 "Name":"mike" "Transaction Value":2 "Running Total ":7>]]
结果如下:
_ #=> 5
arr
#=> [#<CSV::Row "ID":5 "Name":"mike" "Transaction Value":5 "Running Total ":5>]
last_row
#=> #<CSV::Row "ID":5 "Name":"mike" "Transaction Value":2 "Running Total ":7>
Enumerator#next .
我遵循了对块计算中使用的块变量使用下划线的惯例(以提醒读者您的代码)。请注意,下划线是有效的块变量。 1
使用 Enumerable#slice_when代替 chunk
CSV.foreach(CSVFile, headers:true, converters: :numeric).
slice_when { |row1,row2| row1['ID'] != row2['ID'] }.
each do |*arr, last_row|
arr.each { |row| process_row(row, false) }
process_row(last_row, true)
end
这显示了在 chunk 时产生的相同信息。用来。
使用 Kernel#loop单步执行枚举器 CSV.foreach(CSVFile, headers:true)
enum = CSV.foreach(CSVFile, headers:true, converters: :numeric)
row = nil
loop do
row = enum.next
next_row = enum.peek
process_row(row, row['ID'] != next_row['ID'])
end
process_row(row, true)
这显示了在 chunk 时产生的相同信息。用来。见 Enumerator#nextEnumerator#peek .
enum.next返回最后一个 CSV::Row对象 enum.peek将生成 StopIteration异常(exception)。正如其文档中所述, loop通过跳出循环来处理该异常。 row必须在进入循环之前初始化为任意值,以便 row循环结束后可见。当时 row将包含 CSV::Row文件最后一行的对象。
1 IRB 为自己的目的使用下划线,导致块变量 _当上面的代码运行时被分配了一个错误的值。

关于ruby - Ruby 中的 CSV 迭代,并按列值分组以获取每组的最后一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64706538/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com