【问题标题】:Remove array element duplicates at any level of hash?删除任何哈希级别的数组元素重复项?
【发布时间】:2013-07-16 23:25:06
【问题描述】:

具有复杂的多级散列,其中一些值是数组而其他不是,如何在此类散列的任何级别删除重复的数组元素?

只是简单的哈希示例(实际上它要复杂得多):

$VAR1 = {
  'alpha' => {
    'code' => [
      {
        'x' => 1,
        'y' => 2
      },
      {
        'x' => 1,
        'y' => 2
      }
    ],
    'data' => {
      'domestic' => [
        {
          'a' => 0,
          'b' => 5
        },
        {
          'a' => 0,
          'b' => 5
        }
      ]
    }
  }
}

Hash 包含不同级别的数组,其中一些具有 uniq 元素,其中一些包含重复元素。有时这样的数组元素本身就是复杂的哈希。

在任何级别删除任何大小的重复项的正确方法是什么?

【问题讨论】:

  • 对于 alpha.code 中的条目,你是否知道这些条目总是有 'x' 和 'y' 作为元素,或者在 alpha.data.domestic 下,它们总是有 'a '和'b'?
  • @Horus - 特定数组的每个元素的结构都相同...
  • 基于此,我在下面生成了一个您可能想要查看的 OO 响应。您也可以从其他响应中进行 Data::Compare。我更喜欢 OO 版本,因为我认为复杂深度的对象不应该表示为非模式的 hashrefs 和 arrayrefs。

标签: arrays perl hash duplicates


【解决方案1】:

此代码使用Data::Compare 模块,似乎可以满足您的需求。

它递归地遍历数据结构,并使用模块中的Compare 函数检查它到达的每个数组是否存在重复项。找到的重复项将被删除。

use strict;
use warnings;

use Data::Compare 'Compare';

my %data = (
  alpha => {
    code => [{ x => 1, y => 2 }, { x => 1, y => 2 }],
    data => { domestic => [{ a => 0, b => 5 }, { a => 0, b => 5 }] },
  },
);

process_node(\%data);

use Data::Dump;
dd \%data;

sub process_node {

  my ($data) = @_;

  if (ref $data eq 'HASH') {
    process_node($_) for values %$data;
  }
  elsif (ref $data eq 'ARRAY') {

    my $i = 0;
    while ($i < @$data-1) {
      my $j = $i + 1;
      while ($j < @$data) {
        if (Compare(@{$data}[$i,$j])) {
          splice @$data, $j, 1;
        }
        else {
          $j++;
        }
      }
      $i++;
    }

    process_node($_) for @$data;
  }
}

输出

{
  alpha => {
    code => [{ x => 1, y => 2 }],
    data => { domestic => [{ a => 0, b => 5 }] },
  },
}

【讨论】:

  • 即使从简短的角度来看,这段代码看起来也很棒,模块Data::Compare在变量类型JSON::XS::Boolean上崩溃Compare.pm189
【解决方案2】:

我不喜欢没有对象化的深层对象,幸运的是,Moose 内置了强制功能,因此您几乎可以像魔术一样对象化深层对象。

我有点过火了,但我决定继续并把它记下来作为自己的练习,尽管我认为我可以“扮演”一些项目并获得更好的结果,或者强迫 Alpha 强制执行: :Keyed 以从必填字段构建结果类,无论如何。

我并不完全喜欢我编写此代码的方式,但我不想在上面花费大量时间,但它适用于您上面的对象。您必须做很多工作才能让它在更复杂的对象上运行,并且您需要将代码分解为单独的类:

Alpha.pm:

package Alpha;

use Moose;
use Moose::Util::TypeConstraints;

subtype 'AlphaCodes',
    as 'Alpha::Codes';

subtype 'AlphaData',
    as 'Alpha::Data';

coerce 'AlphaCodes',
    from 'ArrayRef[HashRef]',
    via { Alpha::Codes->new( data => $_ ) };

coerce 'AlphaData',
    from 'HashRef',
    via { Alpha::Data->new($_) };

has 'code' => (
    is => 'ro',
    isa => 'AlphaCodes',
    required => 1,
    coerce => 1);

has 'data' => (
    is => 'ro',
    isa => 'AlphaData',
    required => 1,
    coerce => 1);

package Alpha::Codes;

use Moose;
use Moose::Util::TypeConstraints;

extends 'Alpha::KeyedList';

subtype 'ArrayRefOfCodes',
    as 'ArrayRef[Alpha::Code]';

coerce 'ArrayRefOfCodes',
    from 'ArrayRef[HashRef]',
    via { [ map { Alpha::Code->new($_) } @$_ ] };

has 'data' => (
    is => 'ro',
    isa => 'ArrayRefOfCodes',
    required => 1,
    coerce => 1);

package Alpha::KeyedList;

use Moose;
use Moose::Util::TypeConstraints;

sub unique_list {
    my $self = shift;
    my %seen = ();
    my @retval = ();
    foreach my $item ( @{$self->data} ) {
        unless ( $seen{$item->key} ) {
            push(@retval,$item);
            $seen{$item->key} = 1;
        }
    }
    return @retval;
}

package Alpha::Data;

use Moose;
use Moose::Util::TypeConstraints;

subtype 'AlphaDataDomestics',
    as 'Alpha::Data::Domestics';

coerce 'AlphaDataDomestics',
    from 'ArrayRef[HashRef]',
    via { Alpha::Data::Domestics->new(data => $_) };

has 'domestic' => (
    is => 'ro',
    isa => 'AlphaDataDomestics',
    required => 1,
    coerce => 1 );

package Alpha::Data::Domestics;

use Moose;
use Moose::Util::TypeConstraints;

extends 'Alpha::KeyedList';


subtype 'ArrayRefOfDomestics',
    as 'ArrayRef[Alpha::Data::Domestic]';

coerce 'ArrayRefOfDomestics',
    from 'ArrayRef[HashRef]',
    via { [ map { Alpha::Data::Domestic->new($_) } @$_ ] };

has 'data' => (
    is => 'ro',
    isa => 'ArrayRefOfDomestics',
    required => 1,
    coerce => 1);

package Alpha::Data::Domestic;

use Moose;

extends 'Alpha::Keyed';

has 'a' => ( is => 'ro' , isa => 'Str' , required => 1 );
has 'b' => ( is => 'ro' , isa => 'Str' , required => 1 );

sub build_key {
    my $self=  shift;
    return $self->a . '__' . $self->b;
}

package Alpha::Code;

use Moose;

extends 'Alpha::Keyed';

has 'x' => ( is => 'ro' , isa => 'Str' , required => 1 );
has 'y' => ( is => 'ro' , isa => 'Str' , required => 1 );

sub build_key {
    my $self=  shift;
    return $self->x . '__' . $self->y;
}

package Alpha::Keyed;

use Moose;

has 'key' => ( is => 'ro'
    , isa => 'Str'
    , builder => 'build_key'
    , lazy => 1 );

package main;

my $VAR1 = {
  'alpha' => {
    'code' => [
      {
        'x' => 1,
        'y' => 2
      },
      {
        'x' => 1,
        'y' => 2
      }
    ],
    'data' => {
      'domestic' => [
        {
          'a' => 0,
          'b' => 5
        },
        {
          'a' => 0,
          'b' => 5
        },
        {
          'a' => 1,
          'b' => 2
        },
      ]
    }
  }
};

my $alpha = Alpha->new($VAR1->{alpha});

use Data::Dumper;
warn Dumper([ $alpha->code->unique_list ]);
warn Dumper([ $alpha->data->domestic->unique_list ]);

1;

现在开始运行:

$VAR1 = [
      bless( {
               'y' => 2,
               'x' => 1,
               'key' => '1__2'
             }, 'Alpha::Code' )
    ];
$VAR1 = [
      bless( {
               'a' => 0,
               'b' => 5,
               'key' => '0__5'
             }, 'Alpha::Data::Domestic' ),
      bless( {
               'a' => 1,
               'b' => 2,
               'key' => '1__2'
             }, 'Alpha::Data::Domestic' )
    ];

【讨论】:

    【解决方案3】:

    我会在这里看到问题的答案:How can I compare arrays in Perl?

    使用它,您应该能够遍历哈希的所有级别并比较数组级别中的数组。您当然需要为每个可能的数组配对执行此操作。

    如果您可以更好地为您的数组分配键,以便通过某种方式识别它们,那么您就不必担心这一点,因为每个键都必须是唯一的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-13
      • 1970-01-01
      • 2019-09-20
      • 1970-01-01
      • 2018-07-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多