Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

I have a script that I need to write that will clean up an /etc/hosts file which has lines of the format
IPADDRESS alias1 alias2 alias5 IPADDRESS2 alias3 alias4 IPADDRESS2 alias3 alias5 IPADDRESS2 alias3 alias5
for example and I need to rewrite it using a perl script to look like
IPADDRESS alias1 alias2 alias5 IPADDRESS2 alias3 alias4 alias5
Right now I have code to read the file line by line, and split that line. This new array of each line is what I need to work with.

I need to create a
@masterarray
that will contain an array of arrays for example, my final masterarray should be
masterarray[0] = [ IPADDRESS, alias1, alias2, alias5] masterarray[1] = [ IPADDRESS2, alias3, alias4, alias5]
Is this possible.

My code when looping through each line of the file was originally going to first check if the first field in masterarray[0], masterarray[1], masterarray[x] and so on, was the same as the first part of my linearray (gotten from split) but I can't seem to read IPADDRESS out of masterarray[0] = [ IPADDRESS, alias1, alias2, alias5]

How do i read it... Can I do masterarray[0, 0] to read it? and also how can I add an extra alias to masterarray [0] for example...like alias8, can't I just

push (masterarray[0] , alias8)
I get lots of errors when trying to do what I want but I am only a starter in Perl, hopefully one of you can help me out even a little bit!!

Thanks alot guys!
Mark

holli fixed formatting

Replies are listed 'Best First'.
Re: Perl and arrays of arrays?
by ikegami (Patriarch) on Aug 17, 2005 at 13:48 UTC
    You want a hash of hash, not an array of array. Hashes are great for detecting and/or eliminating duplicates.
    my %hosts; while (<IN_FILE>) { chomp; my ($ip, @aliases) = split(/\s+/, $_); foreach my $alias (@aliases) { $hosts{$ip}{$alias} = 1; } } foreach my $ip (sort keys %hosts) { my @aliases = sort keys %{$hosts{$ip}}; print OUT_FILE ("$ip ", join(' ', @aliases), "\n"); }
Re: Perl and arrays of arrays?
by aukjan (Friar) on Aug 17, 2005 at 13:50 UTC

    I would attack this with a hash to filter out duplicates:
    while(<FH>){ chomp; my @fields = split; my $ip = shift @fields; foreach ( @fields ){ $masterhash{$ip}{$_} = 1; } }
    Now the hash will contain an unique IP as key with a hash as value of which the keys are uniqe aliases..
    So now just print it out:
    for my $ip ( keys %masterhash ){ print "$ip ", join (" ",keys %{$masterhash{$ip}}),"\n"; }
    Hope this helps... (not tested)

    Go Fish!

Re: Perl and arrays of arrays?
by QM (Parson) on Aug 17, 2005 at 13:50 UTC
    First, show the code you tried. You'll learn faster on your own code than someone else's.

    Looks like you need a hash of hashes instead. Something like this (untested):

    my %master; while (<>) { my @line = split ' '; foreach my $alias ( @line[1..$#line] ) { $master{$line[0]}{$alias}++; } } foreach my $ip ( sort keys %master ) { print "$ip ", +join( ' ', sort keys %{$master{$ip}}), "\n"; }
    You can use an array for the top level instead, and have an array of hashes. But if you have duplicate aliases, you'll want the hashes at the second level.

    -QM
    --
    Quantum Mechanics: The dreams stuff is made of

      Here is what it looks like as a hash of arrays. It's not as scalable as a hash of hashes, since it needs to grep through the array of aliases each time, but on small files you shouldn't notice.
      use strict; # Hash of arrays # USAGE: $masterhash{$ipaddress} = @aliases my %masterhash; my $input_file = "test.in"; my $output_file = "test.out"; open INPUT_FILE, "<$input_file" or die "Could not open $input_file\n"; # Loop through each line in the input file while(my $line = <INPUT_FILE>) { # Split line on spaces my @line_parts = split(/\s/, $line); # Grab the IP address my $ipaddress = shift @line_parts; # Loop through the aliases foreach my $alias (@line_parts) { # Search through the array for that IP address to determine # whether or not the current alias is already there # # The value in $masterhash{$ipaddress} is an array reference, so # we have to wrap it with a @{} to use it as an array unless(grep {$_ eq $alias} @{$masterhash{$ipaddress}}) { # If we get here, its a new alias for this IP address so add it push @{$masterhash{$ipaddress}}, $alias; } } } # Printing output... open OUTPUT_FILE, ">$output_file" or die "Could not open $output_file\ +n"; # Print one line per IP address foreach my $ip (sort keys %masterhash) { # Print the IP, followed by all of its aliases joined by spaces. print OUTPUT_FILE ("$ip ", join(" ", sort @{$masterhash{$ip}}), "\n" +); }

      test.in
      IPADDRESS alias1 alias2 alias5 IPADDRESS2 alias3 alias4 IPADDRESS2 alias3 alias5 IPADDRESS2 alias3 alias5

      test.out
      IPADDRESS alias1 alias2 alias5 IPADDRESS2 alias3 alias4 alias5
      Hope this helps!
        (Was your reply misplaced?)

        Your comment is wrong, but your code works:

        # USAGE: $masterhash{$ipaddress} = @aliases
        That's the same as:
        $masterhash{$ipaddress} = scalar @aliases
        What you want in this case is:
        $masterhash{$ipaddress} = \@aliases
        The only justification for array of hashes I can come up with is if the number of entries is very large, and you manage duplicate hosts. (For example, the file is already sorted by host, and you keep track of the last host. If the current host is the same, the new data is added to the last host entry. Otherwise, a new host entry is created first.) But searching the host array to find the correct entry on a large array is problematic.

        -QM
        --
        Quantum Mechanics: The dreams stuff is made of

Re: Perl and arrays of arrays?
by halley (Prior) on Aug 17, 2005 at 13:47 UTC
    Of course, the documentation (perldoc perllol) will give a bit more information, but what you're looking for is quick:
    $ip0 = $masterarray[0][0]; push(@{$masterarray[0]}, 'alias8');

    --
    [ e d @ h a l l e y . c c ]

Re: Perl and arrays of arrays?
by monarch (Priest) on Aug 17, 2005 at 13:49 UTC
    use strict; # start by extracting all unique information from file my %addresses = (); while (<>) { my ( $addr, @aliases ) = split( /\s+/, $_ ); foreach my $alias ( @aliases ) { $addresses{$addr}->{$alias} = 1; } } # write out unique information in requested format my @masterarray = (); foreach my $addr ( keys %addresses ) { my @aliases = (); foreach my $alias ( keys %{$addresses{$addr}} ) { push( @aliases, $alias ); } push( @masterarray, [ $addr, @aliases ] ); } # print out array foreach my $masterline ( @masterarray ) { print( join( ' ', @{$masterline} ) ); print( "\n" ); }

    When supplied with a data file matching your input it outputs the following:

    C:\temp>perl -w test.pl testdata.txt IPADDRESS alias1 alias2 alias5 IPADDRESS2 alias3 alias4 alias5