Slice an array of hashes?

Discussion in 'Perl Misc' started by Graham, Sep 10, 2003.

  1. Graham

    Graham Guest

    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.
    Graham, Sep 10, 2003
    #1
    1. Advertising

  2. Graham

    Matija Papec Guest

    X-Ftn-To: Graham

    (Graham) wrote:
    > 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;



    --
    Matija
    Matija Papec, Sep 10, 2003
    #2
    1. Advertising

  3. Graham wrote:
    > 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.
    Chief Squawtendrawpet, Sep 10, 2003
    #3
  4. Graham wrote:
    >
    > 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
    --
    use Perl;
    program
    fulfillment
    John W. Krahn, Sep 10, 2003
    #4
  5. Graham

    Graham Guest

    news:<>...
    >> #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!
    Graham, Sep 10, 2003
    #5
  6. (Graham) wrote in message news:<>...
    > 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
    Quantum Mechanic, Sep 10, 2003
    #6
  7. Graham

    Matija Papec Guest

    X-Ftn-To: Graham

    (Graham) wrote:
    >>> #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. ;)


    --
    Matija
    Matija Papec, Sep 11, 2003
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ben Holness

    Hashes of Hashes via subs

    Ben Holness, Oct 5, 2003, in forum: Perl
    Replies:
    8
    Views:
    560
    Ben Holness
    Oct 8, 2003
  2. kazaam
    Replies:
    12
    Views:
    270
    Matthias Wächter
    Sep 13, 2007
  3. Matt Brooks
    Replies:
    16
    Views:
    227
    Matt Brooks
    Sep 16, 2009
  4. Tim O'Donovan

    Hash of hashes, of hashes, of arrays of hashes

    Tim O'Donovan, Oct 27, 2005, in forum: Perl Misc
    Replies:
    5
    Views:
    209
  5. Slice of a hash of hashes

    , Aug 10, 2006, in forum: Perl Misc
    Replies:
    2
    Views:
    91
    Mumia W.
    Aug 10, 2006
Loading...

Share This Page