0

I am trying to compare all the array values (complete array) with a hash's value(which is array) and if the match founds,then push the key of hash to new array. The below code compare if the hash value is not array but how can I compare if its array?

%hash=(
       storeA=>['milk','eggs'],
       storeB=>['milk','fruits','eggs','vegetables'],
       storeC=>['milk','fruits','eggs'],
      );

@array = (
        'fruits',
        'milk',
        'eggs'
    );

Code to compare

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

foreach my $thing (@array)   {
     foreach ((my $key, my $value) = each %hash)   { 
                 if ($value eq $thing)   {
                       push @new_array, $key;
                }
          }
   }
  print Dumper(\@new_array);

Expected Output

@new_array = (
               storeB,
               storeC
       );
0

5 Answers 5

2

You could also use a combination of all and any form List::Util :

while ((my $key, my $value) = each %hash) {
    if ( all { my $temp = $_; any { $_ eq $temp } @$value } @array ) {  
        push @new_array, $key;
    }
}

So here you are looking for the case where all the elements of @array exists in the given array from the hash.

Sign up to request clarification or add additional context in comments.

Comments

1

I would build a hash out of each store's stock array. It's a wasteful method, but not egregiously so as long as the real data isn't enormous

Like this. The inner grep statement counts the number of items in @list that are available at this store and compares it to the number of items in the list, returning true if everything is in stock

If this is a real situation (I suspect it's homework) then for all practical purposes that I can think of, the %stocks hash should contain hashes of the items available at each store

use strict;
use warnings 'all';

my %stocks = (
    storeA => [ qw/ milk eggs / ],
    storeB => [ qw/ milk fruits eggs vegetables / ],
    storeC => [ qw/ milk fruits eggs / ],
);

my @list = qw/ fruits milk eggs /;

my @stores = grep {
    my %stock = map { $_ => 1 } @{ $stocks{$_} };
    grep($stock{$_}, @list) == @list;
} keys %stocks;

use Data::Dump;
dd \@stores;

output

["storeB", "storeC"]

Comments

1

Find the intersection of the two sets, if the number of its elements is the number of the elements in the array, you want to store the key:

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


sub intersect {
    my ($arr1, $arr2) = @_;
    my %intersection;
    $intersection{$_}{0}++ for @$arr1;
    $intersection{$_}{1}++ for @$arr2;
    return grep 2 == keys %{ $intersection{$_} }, keys %intersection
}


my %hash = (
            storeA => [qw[ milk eggs ]],
            storeB => [qw[ milk fruits eggs vegetables ]],
            storeC => [qw[ milk fruits eggs ]],
           );

my @array = qw( fruits milk eggs );

my @new_array;

while (my ($store, $arr) = each %hash) {  # while, not for!
    push @new_array, $store if @array == intersect(\@array, $arr);
}

use Data::Dumper;
print Dumper(\@new_array);

Comments

1

Simply try this. One small trick i done here. grep was use to filter the element from an array.

I created the variable $joined_array which contain the | separated @array data. Then i pass the variable into the grep.

And the trick is, when the array is compare with a scalar data, the comparison is behave, the total number of an array element with scalar data.

my @array = qw(one two three);

if(@array == 3)
{
    print "Hi\n";

}

Here condition is internally run as 3 == 3.

That the same logic i done here.

use warnings;
use strict;
my %hash=(
    "storeA"=>['milk','eggs'],
    "storeB"=>['milk','fruits','eggs','vegetables'],
    "storeC"=>['milk','fruits','eggs'],
);

my @array = (
    'fruits',
    'milk',
    'eggs'
);

my @new_array;

my $joined_array = join("|",@array);

foreach (keys  %hash)
{

 push(@new_array,$_) if  ((grep{ /\b$joined_array\b/ } @{$hash{$_}}) >= scalar @array);

}

print "$_\n" for @new_array

6 Comments

A nice idea, but if storeC stocked eggshells then it would fulfill eggs on the list. It would also be better to use @array instead of 3, as it expresses the meaning as well as the value, and also continues to work if the contents of @array are changed. And I wonder why you've chosen to put the constant first in the comparison? It's not wrong at all, but it's very uncommon to use 3 <= $expression rather than $expression >= 3.
Oh, and I wouldn't go changing the values of built-in variables like $,, especially without wrapping the associated code in a block and using local. No one remembers what they do, and print "$_\n" for @new_array is fine here
@Borodin Thank you for your helpful comment post edited. I used word bountry \b for matching. And then reason for >= 3 is some time hash of array value contain repeated data something like ` storeB=>['milk','fruits','eggs','vegetables','milk]` my condition will satisfy, ==3 won't satisfy for the above data. :).
I'm not clear about your reasoning for using >=. grep will always return the number of elements in the array if they all match, regardless of duplicates. perl -E 'say scalar grep /a/, qw/ a a a a/' prints 4
@Borodin Consider for example storeB=>['milk','fruits','eggs','vegetables','milk] now grep results the value 4. If 3==4 means not satisfied. So i put the >=.
|
0

Go through all stores (keys) and for each check whether all array elems are in the key's array-ref.

use strict;
use warnings;

my %inventory = (
    storeA => ['milk','eggs'],
    storeB => ['milk','fruits','eggs','vegetables'],
    storeC => ['milk','fruits','eggs'],
);    
my @items = ('fruits', 'milk', 'eggs');

my @found;
foreach my $store (keys %inventory) {
    push @found, $store 
        if @items == grep { in_store($_, $inventory{$store}) } @items
}   

sub in_store { for (@{$_[1]}) { return 1 if $_[0] eq $_ }; return 0; }

print "@found\n";  # prints:  storeB storeC

The grep block checks for each item whether it is (available) in the store, and if the number of those that pass is equal to the number of items (array size) that store has all items and is added. Note that a subroutine returns the last value evaluated without an explicit return, so the final return is not needed. It was added for clarity.

1 Comment

Or, sub in_store { $_[0] eq $_ and return 1 for @{$_[1]} }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.