FAQ
I'm trying to sort a shopping cart basket on the item numbers. The basket is stored in a hash. There is a hashref called %{$main::global->{cart}} that Data::Dumper prints out like so:

$VAR1 = {
'1' => '1 1 SL-8206 73.15 Label 8.25" x 6" 0.8 73.15 ',
'3' => '3 1 WR-9253 13.98 Label - 3" x 9" 0.5 13.98 ',
'2' => '2 1 APT-46V 96.43 4"x6" Label, hook 1.8 96.43 '
};

I would like to grab the item numbers, i.e. SL-8206, to sort the cart on. Could someone point me to something to read up on this? What is the above actually? It looks to me like a hash of hashes of arrays(?).

I thought that converting it into an array would help, like so:

my $n = keys($main::global->{cart});
my @cart_lineitems = @{$main::global->{cart}}{0 .. $n};

but dumping the output:

print {$tracelog_fh} Dumper($cart_lineitems[0]);
print {$tracelog_fh} Dumper($cart_lineitems[1]);
print {$tracelog_fh} Dumper($cart_lineitems[2]);
print {$tracelog_fh} Dumper($cart_lineitems[3]);

gives me this:

$VAR1 = '';
$VAR1 = '1 1 SL-8206 73.15 Label 8.25" x 6" 0.8 73.15 ';
$VAR1 = '2 1 APT-46V 13.98 Label - 3" x 9" 1.8 13.98 ';
$VAR1 = '3 1 WR-9253 96.43 4"x6" Label, hook 0.5 96.43 ';

Why the first one is empty is beyond me, but that's another story. I then tried to grab just the item numbers using both:

print {$tracelog_fh} Dumper($cart_lineitems[1][2]);

and

print {$tracelog_fh} Dumper($main::global->{cart}->{1}->{2});

but they produce these errors:

Can't use string ("1 1 SL-8206 73.15 Label "...) as an ARRAY ref while "strict refs" in use

Can't use string ("1 1 SL-8206 73.15 Label "...) as an HASH ref while "strict refs" in use

Anyway, maybe I'm going about this the wrong way. I know enough just to be dangerous at this point. The bottom line is, how would I go about grabbing the item numbers from the original hashref so that I can sort the cart on them? I'm over my head with this one, so any pointers would be extremely helpful. I'm reading about references in the "Beginning Perl" book, but so far the light hasn't come on.

Thanks,
Marc

Search Discussions

  • Jim Gibson at Aug 3, 2011 at 7:19 pm
    On 8/3/11 Wed Aug 3, 2011 11:12 AM, "Marc" <sono-io@fannullone.us>
    scribbled:
    I'm trying to sort a shopping cart basket on the item numbers. The basket is
    stored in a hash. There is a hashref called %{$main::global->{cart}} that
    Data::Dumper prints out like so:

    $VAR1 = {
    '1' => '1 1 SL-8206 73.15 Label 8.25" x 6" 0.8 73.15 ',
    '3' => '3 1 WR-9253 13.98 Label - 3" x 9" 0.5 13.98 ',
    '2' => '2 1 APT-46V 96.43 4"x6" Label, hook 1.8 96.43 '
    };

    I would like to grab the item numbers, i.e. SL-8206, to sort the cart on.
    Could someone point me to something to read up on this? What is the above
    actually? It looks to me like a hash of hashes of arrays(?).
    No. It is just a one-level hash. $VAR1 is a reference to that hash. If it
    included any embedded arrays, you would see brackets ([]) in the output. If
    there were any embedded hashes, you would see more than one level of braces
    ({}).

    Here is a program that prints out the items in order of item numbers. The
    key is to extract and compare the item numbers from the hash value (I
    shortened the data lines somewhat to avoid line wrap):

    #!/usr/local/bin/perl
    use strict;
    use warnings;

    my $var = {
    '1' => '1 1 SL-8206 73.15 Label 8.25" x 6" 0.8 73.15',
    '3' => '3 1 WR-9253 13.98 Label - 3" x 9" 0.5 13.98',
    '2' => '2 1 APT-46V 96.43 4"x6" Label, hook 1.8 6.43 '
    };

    for my $item (
    sort { substr($var->{$a},4,7) cmp substr($var->{$b},4,7) }
    keys %$var )
    {
    printf(" %3s %s\n", $item, $var->{$item});
    }

    This can be made more efficient by extracting the keys only once in a method
    known as the "Schwartzian transform".

    <http://en.wikipedia.org/wiki/Schwartzian_transform>
  • Marc at Aug 4, 2011 at 1:38 am
    Jim and Rob,

    Thanks to both of you for your responses and for the code you supplied. That was a big help. I was able to get it to work and I've pasted my code below.
    No. It is just a one-level hash.
    I'm glad you both pointed this out. It appears that I was over thinking it. Once I was able to see that, a lot of other things fell into place.
    my $n = keys($main::global->{cart});
    I think this can't be what you wrote - it wouldn't compile. You probably want

    my $n = keys %{$global->{cart}};
    Surprisingly, it did compile, but I like your way better.
    my @cart_lineitems = @{$main::global->{cart}}{0 .. $n};
    That is one way of doing things, but I am surprised you happened upon it without knowing more about Perl.
    "Happened upon it" is exactly how I found it. =;) Google to the rescue! http://stackoverflow.com/questions/2907270/perl-convert-hash-to-array And it worked - that's why I used it, but I didn't remember that hash keys were strings and not numbers.
    Because the values of the array elements are simple strings, as I explained above, you must find a way of splitting them into individual fields.
    I took Rob's advice, since the data was tab separated, merged it with Jim's code, and came up with this. Separating the sort into a sub helps, since I need to use it in multiple places.


    foreach my $cartitem ( sort sortby_itemid keys %{$main::global->{cart}} ) {
    # list the items...
    }


    sub sortby_itemid {
    (split /\t+/, $main::global->{cart}->{$a})[2] cmp (split /\t+/, $main::global->{cart}->{$b})[2]
    }

    Thanks again for your help, guys. I really appreciate it.

    Marc
  • Rob Dixon at Aug 3, 2011 at 7:30 pm

    On 03/08/2011 19:12, Marc wrote:
    I'm trying to sort a shopping cart basket on the item numbers. The
    basket is stored in a hash. There is a hashref called
    %{$main::global->{cart}} that Data::Dumper prints out like so:

    $VAR1 = {
    '1' => '1 1 SL-8206 73.15 Label 8.25" x 6" 0.8 73.15 ',
    '3' => '3 1 WR-9253 13.98 Label - 3" x 9" 0.5 13.98 ',
    '2' => '2 1 APT-46V 96.43 4"x6" Label, hook 1.8 96.43 '
    };

    I would like to grab the item numbers, i.e. SL-8206, to sort the cart
    on. Could someone point me to something to read up on this? What is
    the above actually? It looks to me like a hash of hashes of
    arrays(?).
    Hi Marc

    Take a look at

    perldoc perlreftut

    and

    perldoc perlref

    to read up on nested data structures. But what you have here is a simple
    hash - there is no nesting at all unless you count {cart} which is an
    element of the hash referenced by $global.

    The hash %{$global->{cart}} has three elements, with keys '1', '3' and
    '2' and values equal to the long string the other side of =>. There is
    nothing special about those values - they are simple strings that happen
    to have several prices of information in them, separated by whitespace.
    I thought that converting it into an array would help, like so:

    my $n = keys($main::global->{cart});
    I think this can't be what you wrote - it wouldn't compile. You probably
    want

    my $n = keys %{$global->{cart}};
    my @cart_lineitems = @{$main::global->{cart}}{0 .. $n};
    That is one way of doing things, but I am surprised you happened upon it
    without knowing more about Perl. You have written a hash slice that
    indexes the array using integers. But hash keys are strings, and it is
    fortunate that Perl converts numbers to strings appropriately for this
    hash. (For instance, the element $global->{cart}{'1'} is different and
    separate from the element $global->{cart}{'01'}.)

    I would write

    my @cart_lineitems;
    foreach my $i (keys %{global->{cart}}) {
    $cart_lineitems[$i] = $global->{cart}{$i};
    }

    although the functionality is equivalent and some would say this way
    involves unnecessary code.
    but dumping the output:

    print {$tracelog_fh} Dumper($cart_lineitems[0]);
    print {$tracelog_fh} Dumper($cart_lineitems[1]);
    print {$tracelog_fh} Dumper($cart_lineitems[2]);
    print {$tracelog_fh} Dumper($cart_lineitems[3]);

    gives me this:

    $VAR1 = '';
    $VAR1 = '1 1 SL-8206 73.15 Label 8.25" x 6" 0.8 73.15 ';
    $VAR1 = '2 1 APT-46V 13.98 Label - 3" x 9" 1.8 13.98 ';
    $VAR1 = '3 1 WR-9253 96.43 4"x6" Label, hook 0.5 96.43 ';

    Why the first one is empty is beyond me, but that's another story.
    The first element is empty because you have assigned values at indices
    [1], [2] and [3]. The first element is at [0] and you have left it
    untouched.
    I then tried to grab just the item numbers using both:

    print {$tracelog_fh} Dumper($cart_lineitems[1][2]);

    and

    print {$tracelog_fh} Dumper($main::global->{cart}->{1}->{2});

    but they produce these errors:

    Can't use string ("1 1 SL-8206 73.15 Label "...) as an ARRAY ref while "strict refs" in use

    Can't use string ("1 1 SL-8206 73.15 Label "...) as an HASH ref while "strict refs" in use
    Because the values of the array elements are simple strings, as I
    explained above, you must find a way of splitting them into
    individual fields. The simple way would be to use the 'split' operator
    to divide the line at whitespace, but the presence of spaces in the
    description field spoils that approach. Without knowing more about your
    data, I can suggest that you split on sequences of two or more
    whitespace characters, like this

    my @cart_lineitems;
    foreach my $i (keys %{$global->{cart}}) {
    $cart_lineitems[$i] = [ split /\s{2,}/, $global->{cart}{$i} ];
    }

    which dumps like this

    @cart_lineitems = (
    undef,
    [
    '1 1 SL-8206',
    '73.15',
    'Label 8.25" x 6"',
    '0.8 73.15 '
    ],
    [
    '2 1 APT-46V',
    '96.43',
    '4"x6" Label, hook',
    '1.8 96.43 '
    ],
    [
    '3 1 WR-9253',
    '13.98',
    'Label - 3" x 9"',
    '0.5 13.98 '
    ]
    );

    Which is reasonable, but you still have trailing spaces at the end of
    the final field, which may or may not be a problem.
    Anyway, maybe I'm going about this the wrong way. I know enough just
    to be dangerous at this point. The bottom line is, how would I go
    about grabbing the item numbers from the original hashref so that I
    can sort the cart on them? I'm over my head with this one, so any
    pointers would be extremely helpful. I'm reading about references in
    the "Beginning Perl" book, but so far the light hasn't come on.
    Grabbing just the item numbers (the third field) doesn't involve any
    column that can contain embedded spaces, so you can just sort on the
    value of

    ( split ' ', $lineitem )[2]

    as shown in the program below. Depending on what else you want to do
    with your data, it may well be better to store it in a different format,
    but I hope this serves to help you with your initial problem.

    Cheers,

    Rob


    use strict;
    use warnings;

    use Data::Dumper;

    our $global;
    $global->{cart} = {
    '1' => '1 1 SL-8206 73.15 Label 8.25" x 6" 0.8 73.15 ',
    '3' => '3 1 WR-9253 13.98 Label - 3" x 9" 0.5 13.98 ',
    '2' => '2 1 APT-46V 96.43 4"x6" Label, hook 1.8 96.43 ',
    };

    my @cart_lineitems = sort {
    ( split ' ', $a)[2] cmp ( split ' ', $b)[2];
    } values %{$global->{cart}};

    print Data::Dumper->Dump([\@cart_lineitems], ['*cart_lineitems']);

    **OUTPUT**

    @cart_lineitems = (
    '2 1 APT-46V 96.43 4"x6" Label, hook 1.8 96.43 ',
    '3 1 WR-9253 13.98 Label - 3" x 9" 0.5 13.98 ',
    '1 1 SL-8206 73.15 Label 8.25" x 6" 0.8 73.15 '
    );
  • Rob Dixon at Aug 3, 2011 at 7:51 pm

    On 03/08/2011 20:30, Rob Dixon wrote:
    Because the values of the array elements are simple strings, as I
    explained above, you must find a way of splitting them into
    individual fields. The simple way would be to use the 'split' operator
    to divide the line at whitespace, but the presence of spaces in the
    description field spoils that approach. Without knowing more about your
    data, I can suggest that you split on sequences of two or more
    whitespace characters, like this

    my @cart_lineitems;
    foreach my $i (keys %{$global->{cart}}) {
    $cart_lineitems[$i] = [ split /\s{2,}/, $global->{cart}{$i} ];
    }

    which dumps like this

    @cart_lineitems = (
    undef,
    [
    '1 1 SL-8206',
    '73.15',
    'Label 8.25" x 6"',
    '0.8 73.15 '
    ],
    [
    '2 1 APT-46V',
    '96.43',
    '4"x6" Label, hook',
    '1.8 96.43 '
    ],
    [
    '3 1 WR-9253',
    '13.98',
    'Label - 3" x 9"',
    '0.5 13.98 '
    ]
    );

    Which is reasonable, but you still have trailing spaces at the end of
    the final field, which may or may not be a problem.
    Marc, I have since noticed that your original hash values contain tab
    characters to separate the fields. THis makes things a lot easier, and
    you can write

    my @cart_lineitems = map [ split /\t+/ ], values %{$global->{cart}};

    which now looks like like the dump below.

    Cheers,

    Rob


    @cart_lineitems = (
    [
    '1',
    '1',
    'SL-8206',
    '73.15',
    'Label 8.25" x 6"',
    '0.8',
    '73.15'
    ],
    [
    '3',
    '1',
    'WR-9253',
    '13.98',
    'Label - 3" x 9" ',
    '0.5',
    '13.98'
    ],
    [
    '2',
    '1',
    'APT-46V',
    '96.43',
    '4"x6" Label, hook ',
    '1.8',
    '96.43'
    ]
    );

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupbeginners @
categoriesperl
postedAug 3, '11 at 6:12p
activeAug 4, '11 at 1:38a
posts5
users3
websiteperl.org

3 users in discussion

Marc: 2 posts Rob Dixon: 2 posts Jim Gibson: 1 post

People

Translate

site design / logo © 2021 Grokbase