in reply to Re: Sort the hash array
in thread Sort the hash array

Thank you sir .. I am beginner to perl ,so if you could help to optimized my code with perform below step :: 1. Read the XML file 2. get required nodes using for loop 3. Need to sort this data 4. store the sorted data in array 5. Convert the hash array to JSON 6. Push Json data to httprequest. i will be great help if you can correct where ever in code,I'm missing basic perl syntax and line not relevant..

Replies are listed 'Best First'.
Re^3: Sort the hash array
by haukex (Archbishop) on Jul 06, 2017 at 11:26 UTC
    I am beginner to perl ,so if you could help to optimized my code with perform below step ... i will be great help if you can correct where ever in code,I'm missing basic perl syntax and line not relevant..

    For a beginner, your code looks pretty good, and there are no syntax errors in it (including the commented out code). Are you having problems with it? If so, feel free use the resources of PerlMonks, e.g. Basic debugging checklist and How do I post a question effectively?

    Because the code depends on XML input and servers I don't have available to me, I can't run it, but from a quick read-through I have the following suggestions for improvements:

    • The first line should be #!/usr/bin/perl (or perhaps #!/usr/bin/env perl to use whatever Perl is in the PATH).
    • You don't need both use JSON; and use Cpanel::JSON::XS qw(encode_json);, I think the latter is enough.
    • I don't think you need use DateTime::Duration; or use DateTime::Format::Duration;.
    • As I said, I would suggest you replace my $records = []; with my @records, and then every @$records with @records.
    • You don't seem to be using $count.
    • You do this several times: $var =~ s/\n//; which will remove the first newline character from the string. It's also possible that the XML data might change in the future, so I might recommend the following, more flexible regex, which will trim all whitespace from the beginning and end of the string: s/^\s+|\s+$//g
    • Don't reformat $timereceived with regexes, just adjust the strptime pattern accordingly: '%Y-%m-%dT%H:%M:%S.%3NZ'
    • I suggest you move my $dtnow = DateTime->now; before the loop.
    • This code doesn't match the comment: if ($diff_hours>2000) { # Get the event details which are more then 2 hours
    • While you're free to use a module like Math::Round if you need precise control, note that Perl's sprintf will also do basic rounding for you: sprintf("%.0f h", $diff_hours);
      Sir, I really appreciate your efforts to correct me. I have Incorporated all the suggestion in the code and worked fine. Few more query: --is there way i can use s/^\s+|\s+$//g only once for all the variables and not repetitively ? --if i use sprintf("%.0f h", $diff_hours); gives me err in sorting statement Argument "2158 h" isn't numeric in numeric comparison (<=>) at XPathSample.pl line 55.
      #!/usr/bin/env perl use warnings; use strict; use XML::XPath; use Data::Dumper; use LWP::UserAgent; use DateTime; use DateTime::Format::Strptime; use Cpanel::JSON::XS qw(encode_json); my $xml = 'events.xml'; my $xp = XML::XPath->new(filename => $xml); my $nodeset = $xp->findnodes('//event'); my $json; my @records; my $count=0; # Counter to set the Line variable my $dtnow = DateTime->now; foreach my $node ($nodeset->get_nodelist) { my $severity = $xp->find('./severity', $node); $severity =~ s/^\s+|\s+$//g; # Remove prefix newline \n charac +ter my $ticketnum = $xp->find("./custom_attribute_list/custom_att +ribute[normalize-space(name)='SLB_RemedyIncident']/value", $node); $ticketnum=~ s/^\s+|\s+$//g; my $timereceived = $xp->find('./time_first_received', $node); $timereceived=~ s/^\s+|\s+$//g; my $service_name = $xp->find("./custom_attribute_list/custom_a +ttribute[normalize-space(name)='Service Name']/value", $node); $service_name=~ s/^\s+|\s+$//g; my $ssrid = $xp->find("./custom_attribute_list/custom_attribut +e[normalize-space(name)='SLB_SSRID']/value", $node); $ssrid=~ s/^\s+|\s+$//gs; my $remedyqueue = $xp->find("./custom_attribute_list/custom_at +tribute[normalize-space(name)='SLB_RemedyQueue']/value", $node); $remedyqueue = [ map $_->string_value =~ s/\n//r, $remedyqueue +->get_nodelist ]; #Calculate the time difference my $strp = DateTime::Format::Strptime->new(on_error=>'croak',p +attern => '%Y-%m-%dT%H:%M:%S.%3NZ', time_zone=>'UTC'); my $dtevent = $strp->parse_datetime($timereceived); my $diff_sec = $dtnow->subtract_datetime_absolute($dtevent)->i +n_units('seconds'); my $diff_hours = $diff_sec/(60*60); #print "$diff_sec s / $diff_hours h\n"; if ($diff_hours>2000) { # Get the event details which are more + then 2 hours $count ++; push @records, { line => $count, severity => $severity, ticketnum => $ticketnum, appname => $service_name . "(" . $ssrid . ")", remedy_queue => $remedyqueue, event_age => sprintf("%.0f", $diff_hours), } } @records = sort { $b->{event_age} <=> $a->{event_age} } @recor +ds; # sort the records in decending order of event_age } print Dumper @records; # Convert into JSON format $json = encode_json \@records; # print $json; my $uri = 'http://US1455EPC0674.dir.slb.com:12224/api/submit/3160 +9de09a484e20b9e3a6e5b502be66/dims/line/tags/availability'; my $jsondata = $json; my $req = HTTP::Request->new( 'POST', $uri ); $req->header( 'Content-Type' => 'application/json'); $req->content( $json ); my $lwp = LWP::UserAgent->new; $lwp->request( $req ); print "Data send to Dashboard";
        is there way i can use s/^\s+|\s+$//g only once for all the variables
        s/^\s+|\s+$//g for $severity, $ticketnum, $timereceived, ...;

        Doesn't apply to $remedyqueue though, one way to make that a little bit more idiomatic might be:

        my $remedyqueue = [ map { $_->string_value=~s/^\s+|\s+$//gr } $xp->find("./custom_attribute_list/custom_attribute[normalize-space( +name)='SLB_RemedyQueue']/value", $node) ->get_nodelist ];

        Or, for Perl versions before 5.14: map { (my $x=$_->string_value)=~s/^\s+|\s+$//g; $x }

        if i use sprintf("%.0f h", $diff_hours); gives me err in sorting statement Argument "2158 h" isn't numeric

        You didn't implement the suggestion I showed, instead you're attempting to sort by the field event_age, which does contain strings like "2158 h", which are not plain numbers and therefore can't be compared by the numeric comparison operator <=> <update> cause warnings when you try to use the numeric comparison operator <=> to compare them. </update> I would suggest you implement the suggestion I showed, and if you don't want that field to show up in the JSON, then after the sorting you could do delete $_->{timereceived} for @records;. (While you could theoretically also remove the " h" from the event_age field and then sort that numerically, note that since you've rounded the value, I think you might find that the sorting will be unreliable for events that happened in the same hour.) Also, note that you should move the sort entirely outside the loop, doing it on every iteration is not necessary.