【问题标题】:Find difference between two perl nested hashes查找两个 perl 嵌套哈希之间的差异
【发布时间】:2019-10-01 07:05:41
【问题描述】:

我试图找出两个包含键/值条目的文件的差异,并返回添加或删除所有键/值的内容。目前,我正在使用 linux diff 来查找差异,但是如果值顺序发生更改,那么它将是一个有效的差异,但我不想列出它们,因为对我来说它是无效的。

文件 1:

key1    kamal1.google.com kamal2.google.com kamal3.google.com 
key2    kamal4.google.com 

文件2:

key1    kamal1.google.com kamal6.google.com kamal3.google.com 
key3    kamal4.google.com

我需要什么:

  • 显示deleted key2 with values kamal4.google.comadded key3 with kamal4.google.comdeleted kamal2.google.com from key1added kamal6.google.com to key1
  • 消息具有代表性,我们可以将其修改为更有意义的消息

我的方法是什么:

  • 读取文件并放入不同的哈希值key1 => {kamal1.google.com => 1, ...}, key2 => {kamal4.google.com => 1}。我也将数组作为哈希值,以便我们有效地进行差异。
  • 循环遍历这两个哈希的键并查找它是否存在于哪个哈希中。
  • 进行递归调用以查找值的差异(因为它又是一个哈希)

我的代码有问题:
- 不适用于嵌套
- 失去父母的踪迹。

代码:

my $file1 = 'file1';
my $file2 = 'file2';

my $old = hashifyFile($file1);
my $new = hashifyFile($file2);
my $result = {};
compareHashes($old , $new, $result);
print Dumper $result;

    sub compareHashes {
        my ($hash1, $hash2, $result) = @_;

            for my $key (keys %$hash1, keys %$hash2) {
                if (not exists $hash2->{$key}) {
                        push @{$result->{deleted}->{$key}}, keys %{$hash1->{$key}};
                } elsif (not exists $hash1->{$key}) {
                        push @{$result->{added}->{$key}}, keys %{$hash2->{$key}};
                } elsif (ref $hash1->{$key} eq 'HASH' or ref $hash2->{$key} eq 'HASH' ) {
                    compareHashes($hash1->{$key}, $hash2->{$key}, $result);
                }
            }
    }

# helper functions
sub trim {
   my $val = shift;
   $val =~ s/^\s*|\s*$//g;
   return $val;
}


sub hashifyFile {
    my $file = shift;
    my $contents = {};
    open my $file_fh, '<', $file or die "couldn't open $file $!";

    my ($key, @val);
    while (my $line = <$file_fh>) {
        # skip blank lines and comments
        next if $line =~ /^\s*$/;
        next if $line =~ /^#/;
        # print "$. $line";

        # if line starts with a word, means its "key values"
        # if it starts with multiple spaces assuming minimum 4, seems values for the previous key
        if ($line =~ /^\w/) {
            ($key, @val) = split /\s+|=/, $line;
        } elsif ($line =~ /^\s{4,}\w/) {
            push @val, split /\s+/, $line;
        }
        my %temp_hash;
        for (@val) {
                # next unless $_;
                $temp_hash{trim($_)} = 1 if trim($_);
        }
        $key = trim($key);
        $contents->{$key} = \%temp_hash if defined $key;

    }

    close $file_fh;
    return $contents;
}

【问题讨论】:

  • 请编辑问题并显示来自print Dumper $result的预期输出
  • @HåkonHægland:在我编辑我的代码后,它给出了错误,我还没有修复。所以,我不能给出$result 的输出,但我已经写了我需要的东西。您可以将其称为智能差异,它将在两个文件中显示实际差异。文件不超过 100 行,所以我们可以随意使用辅助数组或哈希。
  • 也许像metacpan.org/pod/Test2::Tools::Compare 这样的东西可能会有所帮助?
  • @Recct:该模块似乎只给出布尔值,无论它们是否相等,这也是由Data::Compare 提供的。但我需要实际价值。我正在探索Data::Diff 模块,这似乎解决了我的问题,但我很想使用自己的代码。

标签: perl data-structures hash set-difference


【解决方案1】:

下面是一个示例,说明如何根据您的描述进行操作。请澄清这是否是您想要的。

sub compareHashes {
    my ($hash1, $hash2, $result, $parent) = @_;

    my %all_keys = map {$_ => 1} keys %$hash1, keys %$hash2;

    for my $key (keys %all_keys) {
        if (not exists $hash2->{$key}) {
            if ( defined $parent ) {
                push @{$result->{deleted}->{$parent}}, $key;
            }
            else {
                push @{$result->{deleted}->{$key}}, keys %{$hash1->{$key}};
            }
        } elsif (not exists $hash1->{$key}) {
            if ( defined $parent ) {
                push @{$result->{added}->{$parent}}, $key;
            }
            else {
                push @{$result->{added}->{$key}}, keys %{$hash2->{$key}};
            }
        }
        else {
            if ((ref $hash1->{$key} eq 'HASH') and (ref $hash2->{$key} eq 'HASH') ) {
                compareHashes($hash1->{$key}, $hash2->{$key}, $result, $key);
            }
        }
    }
}

输出

$VAR1 = {
          'added' => {
                       'key3' => [
                                   'kamal4.google.com'
                                 ],
                       'key1' => [
                                   'kamal6.google.com'
                                 ]
                     },
          'deleted' => {
                         'key2' => [
                                     'kamal4.google.com'
                                   ],
                         'key1' => [
                                     'kamal2.google.com'
                                   ]
                       }
        };

【讨论】:

  • 谢谢,这就是我想要实现的。我开始传递父密钥,但被卡在某个地方。我将使用各种场景对其进行测试,并在需要时更新我的​​代码。 :-)
【解决方案2】:

CPAN 上有几个模块可以比较深度嵌套的数据结构。它们的主要区别在于它们编码差异的方式。这是一个精选列表:

【讨论】:

  • 我尝试了第二和第三个,Data::Diff 解决了我的目的,但我不想使用任何模块。
猜你喜欢
  • 1970-01-01
  • 2016-09-05
  • 2013-08-10
  • 1970-01-01
  • 2019-06-27
  • 2020-12-09
  • 2015-03-19
  • 2014-02-02
  • 2012-02-09
相关资源
最近更新 更多