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

I have a script as below which is taking input from a xml file
#!/usr/bin/perl -w use strict; use XML::Simple; my $servers = XMLin('file.xml'); my %seen; foreach my $server (@{$servers->{server}}) { my $node = $server->{Node} . "\n"; my $lanip = $server->{LanIP} . "\n"; my $mask = $server->{Netmask} . "\n"; substr($lanip, 11, 3) = "0"; push(my @array, $lanip) if ! $seen{$lanip}++; print "@array\n";
Here as you see I am substituting the last digit of the IP with '0' which gives me a bunch of IPs but I only want to grep unique IPs. With current script I get below output
192.169.30.0 192.169.31.0 192.169.32.0 192.169.72.0

Lots of blank spaces. I want to remove them and store it into a global variable/array which can be used later.

The output should be something like below
192.169.30.0 192.169.31.0 192.169.32.0 192.169.72.0
Also if possible I am trying to do this without List::MoreUtils Module.

Replies are listed 'Best First'.
Re: grep unique values, remove the blank spaces and store in a variable
by Athanasius (Archbishop) on Sep 14, 2015 at 07:51 UTC

    Hello deep27ak, and welcome to the Monastery!

    push(my @array, $lanip) if ! $seen{$lanip}++; print "@array\n";

    The logic is wrong. This creates a new @array on each iteration of the foreach loop, conditionally populates it with a single element, and then prints it out whether it contains the element or not. You need something like this (untested):

    my @array; foreach my $server (@{$servers->{server}}) { my $node = $server->{Node} . "\n"; my $lanip = $server->{LanIP} . "\n"; my $mask = $server->{Netmask} . "\n"; substr($lanip, 11, 3) = "0"; push(@array, $lanip) if ! $seen{$lanip}++; } print "@array\n";

    Note also that use of the XML::Simple module in new code is discouraged — see STATUS-OF-THIS-MODULE of XML::Simple.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: grep unique values , remove the blank spaces and store it in a variable
by Anonymous Monk on Sep 14, 2015 at 07:22 UTC
      Thanks for the reply, I tried that method and it works the same way as it is now. The unique values are getting captured but there are a bunch of empty lines
      push(my @array, $lanip) if ! $seen{$lanip}++; #print "@array\n"; my %hash = map { $_, 1 } @array; my @unique = keys %hash; print "@unique\n";
      output
      192.169.30.0 192.169.31.0 192.169.32.0 192.169.72.0
      How can I remove them?

        add another condition to the if/unless

        #!/usr/bin/perl use strict; use XML::Simple; my $xml = do {local $/='';<DATA>} ; my $servers = XMLin($xml); my %seen=(); my @array=(); foreach my $server (@{$servers->{server}}) { my $lanip = $server->{LanIP}; $lanip =~s/\d+$/0/; push(@array, $lanip) unless ( $seen{$lanip}++ || $lanip eq ''); } print join "\n",@array; __DATA__ <xml> <server LanIP="192.169.30.123"/> <server LanIP=""/> <server LanIP="192.169.30.24"/> <server LanIP=""/> <server LanIP="192.169.31.126"/> <server LanIP="192.169.32.127"/> </xml>
        poj
        Hello if you want to filter out, let's say, keys that does not starts whit number, you can change
        my %hash = map { $_, 1 } @array;
        with
        my %hash = map { $_, 1 } grep {$_=~/^\d/} @array;

        Anyway if i understand it must be only one key like this. maybe better you use Data::Dump's dd to inspect the array.

        Also some garbage can come from (deprecated) XML::Simple. Try to switch to something usable like XML::Twig.

        UPDATE: obviously the sage Athanasius is right here below, code does not make much sense: but i cant find where the foreach loop ends, too. Also the substr($lanip, 11, 3) = "0"; part seems very erro prone, maybe something like  s/\d{1,3}$/0/ is better (if i understand the will of OP)
        HtH
        L*
        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
      Actually the seeker's code preserves the input order, but hash would not.
Re: grep unique values , remove the blank spaces and store it in a variable
by BillKSmith (Monsignor) on Sep 14, 2015 at 17:48 UTC
    Your use of the word 'unique' is ambiguous. Do you want those IP's which occur only once in the input or do you want one copy of each IP that occurs in the input? All of the replies so far assume the latter.
    Bill
Re: grep unique values , remove the blank spaces and store it in a variable
by brother_m (Initiate) on Sep 15, 2015 at 21:28 UTC
    The output function adds an extra blank line. We can specify the format, map an array and name the function.
    sub print_as_lines { map { print "$_\n" } @_; }
    This can not remove all the extra lines, because new lines were added while capturing the data, and before processing. The original line preformats the data for presentation. The function print_as_lines encapsulates the presentation operation.

    The program also modifies user input without looking at it first. The following will look before leaping.

    my $lanip = $server->{LanIP}; # no change on block entry # oops, this will format for output before processing $lanip .= "\n" unless $lanip =~ /\n$/; # no change if present #oops, did not validate input
    The following xml has four blank lines, and the original code will make them five.
    <lanIP> 192.168.42.7 </lanIP>
    Do you see them all? The text node includes white space by default.
    my $lanip = $server->{LanIP}; die "Value Error: not dotted quad notation->($lanip)" unless $lanip =~ + /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/; my $lanip = $1; # now this house is clean
    Now you can add another function and wrap the for loop in a function.
    sub unique_network { ... for each return @array } sub print_address { print_as_lines @_: } print_address( unique_network($servers) );