【问题标题】:How to take only latest uniq record based on any column如何仅根据任何列获取最新的唯一记录
【发布时间】:2015-08-06 08:14:20
【问题描述】:

我正在用 perl 编写脚本。但被困在一个部分。以下是我的 csv 文件示例。

"MP","918120197922","20150806125001","prepaid","prepaid","3G","2G"
"GJ","919904303790","20150806125002","prepaid","prepaid","2G","3G"
"MH","919921990805","20150806125003","prepaid","prepaid","2G"
"MP","918120197922","20150806125004","prepaid","prepaid","2G"
"MUM","919904303790","20150806125005","prepaid","prepaid","2G","3G"
"MUM","918652624178","20150806125005","","prepaid","","2G","NEW"
"MP","918120197922","20150806125005","prepaid","prepaid","2G","3G"

现在我需要根据第 2 列(即手机号码)获取唯一记录,但只考虑第 3 列的最新值(即时间戳) 例如:手机号码“918120197922”。

"MP","918120197922","20150806125001","prepaid","prepaid","3G","2G"
"MP","918120197922","20150806125004","prepaid","prepaid","2G"
"MP","918120197922","20150806125005","prepaid","prepaid","2G","3G"

它应该选择第三条记录,因为它具有最新的时间戳值(20150806125005)。请帮忙。

附加信息: 很抱歉数据不一致..我现在已经纠正了。 是的,数据是有序的,这意味着最新的时间戳将出现在最新的行中。 我的文件大小超过 1 gb 的另一件事是有什么方法可以有效地做到这一点?在这种情况下,awk 的工作速度会比 perl 快。请帮忙?

【问题讨论】:

  • 如果你做了任何事情来实现这一点。请告诉我们。那会很棒。
  • 您的文件是否已订购?最新的时间戳总是低于最近的时间戳吗?
  • 在您的示例数据中,我没有看到手机号码 918120197922 的三个 MP 记录。您的措辞不一致,并且与提供的数据相矛盾。请提供一致的MCVE

标签: perl sorting uniq


【解决方案1】:

使用Text::CSV 处理CSV 文件。

按第 2 列散列行,仅保留散列中最近的行。

#!/usr/bin/perl
use warnings;
use strict;

use Text::CSV;

my $csv = 'Text::CSV'->new() or die 'Text::CSV'->error_diag;

my %hash;
open my $CSV, '<', '1.csv' or die $!;

while (my $row = $csv->getline($CSV)) {
    my ($number, $timestamp) = @$row[1, 2];

    # Store the row if the timestamp is more recent than the stored one.
    $hash{$number} = $row if $timestamp gt ($hash{$number}[2] || q());
}

$csv->eol("\n");
$csv->always_quote(1);
open my $OUT, '>', 'uniq.csv' or die $!;
for my $row (values %hash) {
    $csv->print($OUT, $row);
}
close $OUT or die $!;

【讨论】:

  • 谢谢。它正在工作,但我的文件大小为 1 gb,因此处理时间太长。有没有其他方法.. 甚至可以使用 shell 或 awk。请建议..
【解决方案2】:

如果您知道您的数据是按时间戳排序的,您可以利用这一点并向后读取它们并将您的任务转换为一个问题以输出每个电话号码的第一次出现。

#!/usr/bin/env perl

use strict;
use warnings;
use autodie;
use Text::CSV_XS;

use constant PHONENUM_FIELD => 1;

my $filename = shift;
die "Usage: $0 <filename>\n" unless defined $filename;

open my $in, '-|', 'tac', $filename;

my $csv = Text::CSV_XS->new( { binary => 1, auto_diag => 1, eol => $/ } );
my %seen;
while ( my $row = $csv->getline($in) ) {
    $csv->print( *STDOUT, $row ) unless $seen{ $row->[PHONENUM_FIELD] }++;
}

如果您希望输出与输入的顺序相同,您也可以写入tac

#!/usr/bin/env perl

use strict;
use warnings;
use autodie;
use Text::CSV_XS;

use constant PHONENUM_FIELD => 1;

my $filename = shift;
die "Usage: $0 <filename>\n" unless defined $filename;

open my $in, '-|', 'tac', $filename;
open my $out, '|-', 'tac';

my $csv = Text::CSV_XS->new( { binary => 1, auto_diag => 1, eol => $/ } );
my %seen;
while ( my $row = $csv->getline($in) ) {
    $csv->print( $out, $row ) unless $seen{ $row->[PHONENUM_FIELD] }++;
}

1GB 在任何体面的硬件上都不是问题。在我的旧笔记本上,处理 29360128 行和 1.8GB 需要 2m3.393s。它超过 230krows/s 但 YMMV。如果您有兴趣在输出中获得引用的所有值,请将 always_quote =&gt; 1 添加到 $csv 构造函数参数。

【讨论】:

    猜你喜欢
    • 2021-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多