Slice an array of hashes?

G

Graham

Is it possible to slice a list of hashes?

I read in data into the @data array as follows:

while ( $i < $numLev )
{
$line = <$fh>; # Take
next line
$line =~ s/^\s+|\s+$//g; # Strip
whitespace
$line =~ s/,/ /g; #
Replace commas
@prf = ( @prf, (split /\s+/, $line) ); # Get the data
$i = $#prf + 1; # Update
total
}

@data = (@data, {"id"=>$id, "units"=>$units,
"data"=>[@prf]});
}

I want to find all the indices of atm where $id = "foo" or "bar" and
extract them into a new data array called data2.

Alas, at this point I cannot even slice into @data and select the "id"
fields.

print $atm[(0..2)]; # Gives: HASH(0x121064)
print @atm[(0..2)]; # Gives:
HASH(0x121064)HASH(0x11a0e0)HASH(0x112ce8)
print @atm[(0..2)]{"id"}; # Gives: compilation error.
print $atm[(0..2)]{"id"}; # Gives: string id tag for index 2

Thanks.
 
M

Matija Papec

X-Ftn-To: Graham

while ( $i < $numLev )
{
$line = <$fh>; # Take
next line
$line =~ s/^\s+|\s+$//g; # Strip
whitespace
$line =~ s/,/ /g; #
Replace commas
@prf = ( @prf, (split /\s+/, $line) ); # Get the data

You'll be beter with
push @prf, ..
rather then
@prf = @prf, ..
$i = $#prf + 1; # Update

$i = @prf is more classic way to do the same thing.
@data = (@data, {"id"=>$id, "units"=>$units,
"data"=>[@prf]});
I want to find all the indices of atm where $id = "foo" or "bar" and
extract them into a new data array called data2.

You don't have atm in above example but I'll /guess/ it looks like @data,

#untested
my @idx = grep $atm[$_]{id} =~ /^(?:foo|bar)$/, 0 .. $#atm;
Alas, at this point I cannot even slice into @data and select the "id"
fields.

print $atm[(0..2)]; # Gives: HASH(0x121064)

This is same as $atm[0], as you're here refering to scalar, not array slice.
btw, you don't need braces around range.
print @atm[(0..2)]; # Gives:
HASH(0x121064)HASH(0x11a0e0)HASH(0x112ce8)
print @atm[(0..2)]{"id"}; # Gives: compilation error.
print $atm[(0..2)]{"id"}; # Gives: string id tag for index 2

perldoc perlref is your friend.
Try,
use Data::Dumper;
print Dumper @atm;
 
C

Chief Squawtendrawpet

Graham said:
I want to find all the indices of atm where $id = "foo" or "bar" and
extract them into a new data array called data2.

Your code uses @data, but your follow-up discussion speaks of @atm. I
assume they are the same.

Things like @atm[0..2]{id} don't make sense and give an error, as you
found out. Before you can get access to the items deeper in the data
structure (say, for the 'id' key), you have to commit to a subscript
at the higher levels; otherwise, Perl doesn't know which 'id' value to
grab.

Something like this would allow you to select items from @atm
according to the 'id' characteristics (code not tested):
@atm2 = grep { $_->{id} eq 'foo' } @atm;

Or if you want indexes to @atm instead of the hash refs themselves
(also not tested):
@ind = grep { $atm[$_]{id} eq 'foo' } 0 .. $#atm;

As a side note, use:
push @data, {HASH};
instead of code like this:
@data = (@data, {HASH});

Chief S.
 
J

John W. Krahn

Graham said:
Is it possible to slice a list of hashes?
Yes.


I read in data into the @data array as follows:

while ( $i < $numLev )
{
$line = <$fh>; # Take next line
$line =~ s/^\s+|\s+$//g; # Strip whitespace
$line =~ s/,/ /g; # Replace commas
@prf = ( @prf, (split /\s+/, $line) ); # Get the data
$i = $#prf + 1; # Update total
}

Better written as:

while ( my $line = <$fh> ) { # Take next line
s/^\s+//, s/\s+$// for $line; # Strip whitespace
$line =~ tr/,/ /; # Replace commas
push @prf, split ' ', $line; # Get the data
last if @prf >= $numLev; # Update total
}

@data = (@data, {"id"=>$id, "units"=>$units, "data"=>[@prf]});

push @data, { id => $id, units => $units, data => [@prf] };

}

I want to find all the indices of atm where $id = "foo" or "bar" and
extract them into a new data array called data2.

Alas, at this point I cannot even slice into @data and select the "id"
fields.

print $atm[(0..2)]; # Gives: HASH(0x121064)
print @atm[(0..2)]; # Gives:
HASH(0x121064)HASH(0x11a0e0)HASH(0x112ce8)
print @atm[(0..2)]{"id"}; # Gives: compilation error.
print $atm[(0..2)]{"id"}; # Gives: string id tag for index 2

for my $hash ( @atm[ 0 .. 2 ] ) {

if ( $hash->{ id } eq 'foo' or $hash->{ id } eq 'bar' ) {

# do stuff with $hash->{ id }
# or $hash->{ units } or $hash->{ data }



John
 
G

Graham

#untested
my @idx = grep $atm[$_]{id} =~ /^(?:foo|bar)$/, 0 .. $#atm;

Thanks a lot Matija! That is very close to what I want. The only
trouble is that 'foo' and 'bar' are given in a command line option and
I cannot seem to build a (for lack of a better word) 'dynamic' regex
that is composed of my command line options.

What is wrong with:

$search = "\^(\?:" . join("|", @search) . ")\$";
my @idx = grep $atm[$_]{"id"} =~ /$search/, 0 .. $#atm;

Thanks again!
 
Q

Quantum Mechanic

Is it possible to slice a list of hashes?

Yes. But not as a literal slice, not the way you intended.

If you build @data like this:

@data = (@data, {"id"=>$id, "units"=>$units, "data"=>[@prf]});

then this will give you a slice:

@slice = @data[0,3,7..19];

But @slice now contains hashrefs, not just the "id" values you wanted.

To get the list of indices matching "foo" or "bar:

@indices = map { $data[$_]{id} =~ /^foo|bar$/ } 0..@data-1;

If instead you want the subset of @data itself:

@subset = map { $_->{id} =~ /^foo|bar$/ } @data;

But syntax like this:

@ids = @data[0..2]{"id"}

is equivlent to:

@ids = $data[2]{"id"}

which is the last index listed in the slice, regardless of the slice
indices or their order. [I tried this on ActivePerl 5.8.0.]

Someone more familiar with perlguts can provide an explanation, but it
appears that once the slice is generated, Perl treats it as a
comma-separated list, so only the last "expression" is returned.

-QM
 
M

Matija Papec

X-Ftn-To: Graham

#untested
my @idx = grep $atm[$_]{id} =~ /^(?:foo|bar)$/, 0 .. $#atm;

Thanks a lot Matija! That is very close to what I want. The only
trouble is that 'foo' and 'bar' are given in a command line option and
I cannot seem to build a (for lack of a better word) 'dynamic' regex
that is composed of my command line options.

What is wrong with:

$search = "\^(\?:" . join("|", @search) . ")\$";
my @idx = grep $atm[$_]{"id"} =~ /$search/, 0 .. $#atm;

There is nothing wrong with your $search, you could eventually remove first
two unneeded backslashes and compile regex for faster execution,
$search = qr/$search/;

...and make sure that @search and @atm contain things that you want there. ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,527
Members
44,998
Latest member
MarissaEub

Latest Threads

Top