Hi,
I have a large dataset in a configuration file which I plan on eventually porting to a more practical format, but for various reasons this will be complex, therefore for now I have to deal with it. Historically, multidimensional data structures have been stored as sets of selector style keys and values. For example:
0.foo.bar = 0foobar 0.foo.baz = 0foobaz 1.foo = 1foo equates to: [ { foo => { bar => '0foobar', baz => '0foobaz' } }, { foo => '1foo' } ]
The data is parsed by splitting the key into its individual components then running them through Data::Diver. The problem is I sometimes need to add data into the middle of the dataset and as a result have to increment index components within keys below this, which can be tedious to say the least. I decided the easiest quick fix would be to pre-process the keys beforehand and automatically calculate the array indexes. Thus the above dataset would look:
>.foo.bar = 0foobar <.foo.baz = 0foobaz >.foo = 1foo
Where '>' means "next index // 0" and '<' means "last index // 0", within scope of the dimension. I wrote the following demo:
use strict; use warnings; use Data::Diver qw#DiveVal DiveError#; use Data::Dumper qw#Dumper#; $/ = qq#\r\n#; my $state = { }; my $ref = undef; while ( my $line = <DATA> ) { next if ( $line =~ qr#^(\#|\s*$)# ); # ignore line if comment or b +lank. chomp $line; # remove newline. my ( $selector, $value ) = split qr#\s*=\s*#, $line; # (selector)= +(value). todo: unless escaped. next if ( not defined $selector ); # ignore line if no selector. my @selector = split qr#\.#, $selector; # (one).(two).(three)... t +odo: unless escaped. $ref //= ( $selector[0] =~ qr#^([><]|\d+)$# ) ? [ ] : { } ; _dive( $ref, \@selector, $value ); } print Dumper $state, $ref; sub _dive { my ( $ref, $selector, $value ) = @_; return if ( not defined $ref or not defined $selector or not scala +r @$selector ); # return if no ref or no selectees. my @selector_b = qw##; for my $selectee ( @$selector ) { if ( $selectee =~ qr#^([><])$# ) # if incognito selectee. todo +: unless escaped. { my $selector_b = join q#.#, @selector_b; if ( $1 eq q#># ) # incognito selectee is of increment typ +e. { if ( defined $state->{$selector_b} ) # we have seen th +is state before. { push @selector_b, $state->{$selector_b} += 1; # pu +sh current index + 1. } else { push @selector_b, $state->{$selector_b} = 0; # pus +h 0 (first) index. } } elsif ( $1 eq q#<# ) # incognito selectee is of maintain t +ype. { push @selector_b, $state->{$selector_b} //= 0; # push +current index or 0 (first) index. } } else # else non inconito selectee. { push @selector_b, $selectee; # push selectee. } } DiveVal( $ref, @selector_b ) = $value; my ( $error ) = DiveError( ); $error and warn $error; return 1; } __DATA__ >.name = john <.location = uk <.interests.> = programming <.interests.> = cycling >.name = laura <.location = <.interests.> = knitting <.interests.> = tennis <.interests.> = dancing >.name <.location = canada <.interests.>.> = dogs <.interests.<.> = horses <.interests.> = cars # test.error = blah
Output:
$VAR1 = { '2.interests' => 1, '' => 2, '2.interests.0' => 1, '0.interests' => 1, '1.interests' => 2 }; $VAR2 = [ { 'interests' => [ 'programming', 'cycling' ], 'location' => 'uk', 'name' => 'john' }, { 'interests' => [ 'knitting', 'tennis', 'dancing' ], 'location' => '', 'name' => 'laura' }, { 'interests' => [ [ 'dogs', 'horses' ], 'cars' ], 'location' => 'canada', 'name' => undef } ];
Is this a good approach, or is there a better alternative? Perhaps I should look into extending Data::Diver's DiveRef function? Can my code be improved, I don't mind a bit of golfing, but I'm concerned there could be a particular scenario that I've missed where my code could break?
Chris
In reply to Dive data with automatic array indexing by peterp
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |