1

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

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

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

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

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

4

3 回答 3

1

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

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

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

阿尔法下午:

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' )
    ];
于 2013-01-13T02:23:51.080 回答
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 }] },
  },
}
于 2013-01-13T06:20:34.313 回答
0

I would see the answer to the question here: How can I compare arrays in Perl?

Using that you should be able to iterate through all levels of your hash and compare the arrays in the array level. You would of course need to do it for each possible pairing of arrays.

If you could better assign keys to your arrays so that it some how identified them then you wouldn't need to worry about this as each key needs to be unique.

于 2013-01-13T01:36:22.790 回答