battlemidget
battlemidget

Reputation: 249

map predefined words in a string into a hash in perl

Basically I'm trying to take certain keywords in a string along with the accompanying text to be put in a hash.

A sample string:

!add-action-item :date 03/29/2012 :task Go to the bathroom :prio 1

Code snip:

when(/^!add-action-item/) {
        my ($date, $prio, $task) = $what =~ /:date\s(\d{2}\/\d{2}\/\d{4})\s|:prio\s(\d+)\s|:task\s(.*)/;
        print Dumper($date, $prio, $task);
    }

So basically I'd like to have predefined attributes like :date, :prio, :task and convert those into

%ash = ( date => $date,
         prio => $prio,
         task => $task
       )

Eventually I'd like to just accept any attribute and place into a key value pair and just act on the ones I care about. Im pretty noobish when it comes to perl so I apologize if this is something builtin I missed in the documentation.

Thanks

Upvotes: 1

Views: 247

Answers (3)

matthias krull
matthias krull

Reputation: 4429

You can not capture all three groups using |.

Fix your regex and then build your data structure from the capturing groups.

use strict;
use warnings;
use Data::Dumper;

my $string = '!add-action-item :date 03/29/2012 :task Go to the bathroom :prio 1';

if ( $string =~ /^!add-action-item/ ) {
    $string =~ m[
        \s+:date\s(\d{2}/\d{2}/\d{4})
        \s+:task\s(.*)
        \s+:prio\s(\d+)
    ]x;

    my $data = {
        date => $1,
        task => $2,
        prio => $3,
    };

    print Dumper $data;
}

__END__

$VAR1 = {
      'date' => '03/29/2012',
      'prio' => '1',
      'task' => 'Go to the bathroom'
};

Upvotes: 3

ikegami
ikegami

Reputation: 385917

I don't like the idea of looking for specific fields in a specific order. mugen kenichi's solution will fail if a 4th field is added, for example.

Here's a more robust solution by virtue of being more generic:

my $cmd = '!add-action-item :date 03/29/2012 :task Go to the bathroom :prio 1';

for ($cmd) {
   /\G ! (\S+) /xgc or die;
   my $action = $1;

   my %args;
   for (;;) {
      last if /\G \s* \z /xgc;

      /\G \s+ :(\S+) /xgc or die;
      my $name = $1;

      /\G \s+ ([^\s:]\S*(?:\s+[^\s:]\S*)*) /xgc or die;
      my $val = $1;

      $args{$name} = $val;
   }

   # Do something with $action and %args here.
}

Alternative implementation:

my $cmd = '!add-action-item :date 03/29/2012 :task Go to the bathroom :prio 1';

{
   my ($action, @args) = split /\s+:/, $cmd;

   $action =~ s/^!//;
   $action =~ s/\s+\z//;

   my %args;
   for my $arg (@args) {
      my ($name, $val) = split(' ', $arg, 2);
      $args{$name} = $val;
   }

   # Do something with $action and %args here.
}

For example, if you use

use Data::Dumper qw( Dumper );
local $Data::Dumper::Indent = 0;
local $Data::Dumper::Terse  = 1;
printf("action: %s\n", $action);
printf("args:   %s\n", Dumper(\%args));

You get:

action: add-action-item
args:   {'date' => '03/29/2012','prio' => '1','task' => 'Go to the bathroom'}

Parse::RecDescent grammar (untested):

parse    : command /\Z/ { $item[1] }
command  : action arg(s) { [ $item[1], { map @$_, @{$item[2]} } ] }
action   : /!\S+/ { substr($item[1], 1) }
arg_name : /:\S+/ { substr($item[1], 1) }
arg_val  : /[^\s:]\S*(?:\s+[^\s:]\S*)*/

Upvotes: 2

JRFerguson
JRFerguson

Reputation: 7516

use Data::Dumper;
my @array = qw(Today stat bigone);
my %hash;
@hash{qw( date prio task )} = @array;
print Dumper \%hash;

$VAR1 = {
          'date' => 'Today',
          'prio' => 'stat',
          'task' => 'bigone'
        };

Upvotes: 2

Related Questions