views:

247

answers:

2

Hello,

I have a function which extracts Excel data into an array of hashes like so:


sub set_exceldata {

    my $excel_file_or = '.\Excel\ORDERS.csv';
    if (-e $excel_file_or) {

        open (EXCEL_OR, $excel_file_or) || die("\n can't open $excel_file_or: $!\n");                   
        while () {

            chomp;
            my ( $id, $date, $product, $batchid, $address, $cost ) = split ",";
            my %a = ( id      => $id
                    , date    => $date
                    , product => $product
                    , batchid => $batchid
                    , address => $address
                    , cost    => $cost
                    );
            push ( @array_data_or, \%a );
        }
        close EXCEL_OR;
    }
}

Populating the array of hashes is fine. However, the difficult part is searching for a particular item (hash) in the array. I can't seem to locate items that might have an id or 21, or a batchid of 15, or a cost > $20 etc.

How would I go about implementing such a search facility?

Thanks to all,

+8  A: 

With the power of grep

my @matching_items = grep {
  $_->{id} == 21
} @array_data_or;

If you know there will be only one item returned you can just do this:

my ($item) = grep {
  $_->{id} == 21
} @array_data_or;

(Untested, and I haven't written one of these in a while, but this should work)

David Dorward
Thanks for your insight David. Regarding your solution this returns an array of results whereas because there will really only be one occurence is should return a hash and not an array. Would this solution still be valid when returning a hash instead of an array of hashes?
if you're sure it will return only one value you can use $matching_items[0], or do my $matching_items = (grep { $_->{id} == 21} @array_data_or)[0];
Nathan Fellman
@DidYouJustDoThat: Assuming that there is only one match is not necessarily safe. Some of your examples (e.g. cost > 20) could return multiple items.
Michael Carman
+5  A: 

If you're sure that the search always returns only one occurence or if you're interested in only the first match then you could use the 'first' subroutine found in List::Util

use List::Util;

my %matching_hash = %{ first { $_->{id} == 21 } @array_data_or };

I enclosed the subroutine call in the %{ } block to ensure that the RHS evaluates to a hash.

muteW
The %{} around the call to first() dereferences the hashref from the array, making a (shallow) copy. That could be a problem depending on how the OP uses it. I'd return the hashref instead: `$hr = first {$_->{id} == 21} @array_data_or`
Michael Carman
I too would prefer to return and later use the hashref but according to DidYouJustDoThat's comment to David Doward's reply, the poster is looking for a hash and not a reference.
muteW
I think saying "hash" is just sloppy terminology. He wants a single value instead of a list.
Michael Carman
Could be, that's why I added the RHS line at the bottom to clarify what I was doing so that the OP could use either the reference value or the entire hash.
muteW