File Coverage

blib/lib/Data/Delete.pm
Criterion Covered Total %
statement 45 55 81.8
branch 19 32 59.3
condition 2 3 66.6
subroutine 8 8 100.0
pod 1 1 100.0
total 75 99 75.7


line stmt bran cond sub pod time code
1 1     1   510 use strict;
  1         2  
  1         38  
2 1     1   6 use warnings;
  1         1  
  1         70  
3              
4             package Data::Delete;
5             $Data::Delete::VERSION = '0.06';
6 1     1   643 use Moo;
  1         14753  
  1         8  
7 1     1   2600 use MooX::Types::MooseLike::Base qw/HashRef Bool/;
  1         8191  
  1         777  
8              
9             =head1 NAME
10              
11             Data::Delete - Delete keys with undefined or empty string values in a deep data structure
12              
13             =head1 SYNOPSIS
14              
15             use Data::Delete;
16             my $dd = Data::Delete->new;
17             my $deep_data_structure = {
18             id => 4,
19             last_modified => undef,
20             sections => [
21             {
22             content => 'h1. Ice Cream',
23             class => 'textile'
24             },
25             {
26             content => '# Pie',
27             class => ''
28             },
29             ],
30             };
31             use Data::Dumper;
32             print Dumper $dd->delete($deep_data_structure);
33              
34             # results in:
35              
36             {
37             id => "4",
38             sections => [
39             {
40             content => 'h1. Ice Cream',
41             class => 'textile'
42             },
43             {
44             content => "# Pie"
45             }
46             ]
47             }
48              
49             =head1 DESCRIPTION
50              
51             A module for when you want to remove HashRef keys when the value is undefined
52             or an empty string.
53              
54             =cut
55              
56             has 'references_seen' => (
57             is => 'rw',
58             isa => HashRef,
59             );
60              
61             =head2 debug_delete
62              
63             Turn on/off debugging
64              
65             =cut
66              
67             has 'debug_delete' => (
68             is => 'ro',
69             isa => Bool,
70             );
71              
72             =head2 will_delete_empty_string
73              
74             Choose to remove empty string hash values
75              
76             =cut
77              
78             has 'will_delete_empty_string' => (
79             is => 'lazy',
80             isa => Bool,
81 1     1   673 builder => sub { 1 },
82             );
83              
84             =head1 METHODS
85              
86             =head2 delete
87              
88             Signature: (HashRef|ArrayRef)
89             Returns: The data structure with undefined hash values, and optionally,
90             empty string hash values removed
91              
92             =cut
93              
94             sub delete {
95 3     3 1 3572 my ( $self, $data ) = @_;
96 3 100       15 if ( ref($data) eq 'HASH' ) {
    50          
97 1         4 return $self->_delete_hash($data);
98             }
99             elsif ( ref($data) eq 'ARRAY' ) {
100 2         5 return $self->_delete_array($data);
101             }
102             else {
103 0         0 die "You must pass the delete method either a HashRef or an ArrayRef";
104             }
105             }
106              
107             sub _delete_hash {
108 7     7   10 my ( $self, $hashref ) = @_;
109              
110             # Work on a copy
111 7         6 my %hashref = %{$hashref};
  7         30  
112 7         10 $hashref = \%hashref;
113              
114 7         8 foreach my $key ( keys %{$hashref} ) {
  7         18  
115 15         79 my $value = $hashref->{$key};
116 15         18 my $ref_value = ref($value);
117 15         318 my $references_seen = $self->references_seen;
118              
119             # Skip if we've seen this ref before
120 15 50 66     495 if ( $ref_value and $references_seen->{$value} ) {
121 0 0       0 warn "Seen referenced value: $value before" if $self->debug_delete;
122 0         0 next;
123             }
124              
125             # If we have a reference value then note it to avoid deep recursion
126             # with circular references.
127 15 100       29 if ($ref_value) {
128 1         3 $references_seen->{$value} = 1;
129 1         15 $self->references_seen($references_seen);
130             }
131 15 100       64 if ( not $ref_value ) {
    50          
    50          
132              
133             # Delete a key when the value is not defined
134 14 100       35 if ( not defined $value ) {
    100          
135 5         13 delete $hashref->{$key};
136             }
137              
138             # Optionally delete an empty string value
139             elsif ( length($value) == 0 ) {
140 3 100       49 delete $hashref->{$key} if $self->will_delete_empty_string;
141             }
142              
143             # Make no change
144             else { }
145             }
146              
147             # Defined and not the zero string
148             elsif ( $ref_value eq 'HASH' ) {
149              
150             # Recurse when a value is a HashRef
151 0         0 $hashref->{$key} = $self->_delete_hash($value);
152             }
153              
154             # Look inside ArrayRefs for HashRefs
155             elsif ( $ref_value eq 'ARRAY' ) {
156 1         3 $hashref->{$key} = $self->_delete_array($value);
157             }
158              
159             # Leave alone
160             else { }
161             }
162 7         35 return $hashref;
163             }
164              
165             sub _delete_array {
166 3     3   6 my ( $self, $arrayref ) = @_;
167              
168 3         70 my $references_seen = $self->references_seen;
169             $arrayref = [
170             map {
171 12 100       21 if ( ref($_) ) {
172 6 50       12 if ( ref($_) eq 'HASH' ) {
    0          
173 6         15 $self->_delete_hash($_);
174             }
175             elsif ( ref($_) eq 'ARRAY' ) {
176              
177             # Skip if we've seen this ref before
178 0 0       0 if ( $references_seen->{$_} ) {
179 0 0       0 warn "Seen referenced value: $_ before"
180             if $self->debug_delete;
181 0         0 $_;
182             }
183             else {
184 0         0 $references_seen->{$_} = 1;
185 0         0 $self->references_seen($references_seen);
186 0         0 $self->_delete_array($_);
187             }
188             }
189             }
190             else {
191 6         13 $_;
192             }
193 3         17 } @{$arrayref}
  3         17  
194             ];
195 3         17 return $arrayref;
196             }
197              
198             1;
199              
200             =head1 AUTHORS
201              
202             Mateu X Hunter C
203              
204             =head1 COPYRIGHT
205              
206             Copyright 2015, Mateu X Hunter
207              
208             =head1 LICENSE
209              
210             You may distribute this code under the same terms as Perl itself.
211              
212             =cut