1

I'm reading in a file of words and need to hash words to a key if they are anagrams. So if I read in dog, I sort the word to become dgo. And this would be my key. So I read in the word god, it would be sorted to dgo as well and they should both hash to the same key.

Here is what I am trying but I am not sure if I am doing this correctly.

    if(exists $hash{$string})
    {
            @values2 = $hash{$string};
            push @values2, $original; 

            for my $word (@values2)
            {
                print $word."\n";
            }
            #print "Hello";
    } 

    else
    {
         @values = ();
        $hash {$string} = @values;  
        push @values, $string; 
    }   
}

So the $string is my sorted word, the key. So if the key doesn't exist, i create a new array at that key for my $hash. i then push the orginal word into the array. But if the key already exists, I then get the array from the hash and push or add the next word.

But this isn't working properly. Can I not do this?

3 Answers 3

1

Basic Perl data structures are about single values. The variable $foo can only store a single value. The array @foo can store an array of single values. The hash %foo has a key that points to a single value.

If you need more (such as a key that points to multiple values), you need to know about Perl References. A Perl reference is a Perl data structure (such as a hash or array) where each entry points not to a single value, but to another structure.

In your case, you'd like your key (the word dgo) to point to an array of words that have those letters.

Imagine something like this:

my @dgo_words = qw(dog dgo god gdo odg ogd);   # All possible combinations
$words{dgo} = \@dgo_words;   # The '\' means this is a reference to @dgo_words

Now, words{dgo} points to a reference to the array @dgo_words. If dereference the reference (by putting the correct prefix on the variable), I can get back to the array:

my @array_of_dgo_words = @{ $words{dgo} };

Remember, $words{dgo} points to an array, and putting the @ in front gives me access to that array. The curly braces in this particular case are optional:

my @array_of_dgo_words = @$words{dgo};

Personally, I prefer the braces because it highlights the fact this is a reference. Others feel that eliminating them makes the code easier to read.

If @{ $words{dgo} } is my array, I could use push to add words to the array:

push @{ $words{dgo} }, 'dog';

Now, dog is added to the array referenced by $words{dgo}.

Here's a simple program:

#! /usr/bin/env perl

use strict;
use warnings;
use feature qw(say);


my %words;
#
# First add all the words into the correct key
#
while ( my $word = <DATA> ) {
    chomp $word;
    my $key = join '', sort split //, $word;
    push @{ $words{$key} }, $word;
}

for my $group ( sort keys %words ) {        # For each key in my word hash
    my @word_group = @{ $words{$group} };   # Dereference to get the list of words
    say qq(Words for group "'$group":);
    for my $word ( @word_group ) {          # This loop prints out the words
        say "    $word";
    }
}

__DATA__
dog
bog
save
god
gob
vase
Sign up to request clarification or add additional context in comments.

Comments

1

The thing you need to understand is: There is no such thing as a two dimensional data structure in perl. You don't have a hash of arrays, you have a hash of array references.

This can lead to some pretty subtle gotchas.

This for example - isn't doing what you think:

@values = ();
$hash{$string} = @values;
push @values, $string;

It's emptying @values. But then it's assigning it in a scalar context. Which means you're setting:

$hash{$string} = 0; 

And then inserting $string into@values, but that's making no difference to the hash, because you've set the hash value to the size of your empty array.

Likewise this:

@values2 = $hash{$string};
push @values2, $original;

for my $word (@values2) {
    print $word. "\n";
}

You're only ever going to be retrieving an array reference at best (but if you've populated with the else block it's not even that - it's just 0) which means your for loop isn't going to work. $hash{$key} can only ever be a single value.

If you want to set a hash key to an array;

 $hash{$string} = [@values]; 

If you want to add elements:

 push ( @{$hash{$string}}, @values ); 

And if you want to extract elements;

my @array = @{ $hash{$string} }; 

You need the extra sigil, because that's how you tell perl 'work with a reference'. ( You can also use -> notation in some circumstances. I've omitted this to avoid confusing the issue )

3 Comments

Thanks @Sobrique. This definitely helps me understand it better. So all i needed to do was to give the reference of the array, which makes sense. But I'm going to do it this way as well for extra practice. Thanks!
I wish people would stop claiming that Perl doesn't support proper multidimensional arrays. It absolutely does: it implements them as arrays of array references. C does it by fixing the size of rows at compile time and doing arithmetic on the indices. If anyone can propose a test for multidimensional arrays that Perl fails then I will take it back. Along with Perl code is ugly this is a lie that particularly riles me, and to find it being repeated by a Perl proponent is dismaying.
I don't think I said that. It certainly wasn't my intent. Merely to clarify the mechanism by which multidimensional structures are implemented. I also agree - lots of people say perl is 'messy' - it's true it lets you get away with more, but you can write bad code in any language.
0

Yes, you can do this. But you cannot store an array in a hash, you have to store a reference to it.

push @{ $hash{$string} }, $original;

To retrieve the array, derefence the value:

print join ' ', @{ $hash{dgo} }; # dog god

1 Comment

OK, I tried that but it doesn't seem to work. When i do push @{$hash{$string}}, $original; It seems to be hashing everything to the same thing. Because when I do the print that you mentioned, it prints the all the words. Like I pretty much took out everything in my if and added the line you mentioned. Or is that wrong? thanks

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.