Perl CGI scrolling list wont print array of Hash - arrays

I am trying to write a Perl CGI script that prints /etc/passwd users but when I open my CGI my scrolling list just prints out multiple lines of this:
"HASH(0x27836d8)"
Here is my code where I grab etc passwd and print it to the scrolling list. Can anyone help me out with printing this correctly to the scrolling list?
setpwent();
while (#list = getpwent())
{
($LOGIN,$PASSWORD,$UID,$GID,$QUOTA,$COMMENT,$GECOS,$HOMEDIR,$SHELL) = #list[0,1,2,3,4,5,6,7,8];
if( $UID >= 1001 )
{
push #users, { login => "$LOGIN"};
}
}
endpwent();
print scrolling_list(-name=>'user_list',
-values=>[#users],
-size=>15);

You gave the list a bunch of hash references, so that's what got displayed. Change
push #users, { login => "$LOGIN"};
to
push #users, $LOGIN;
use strict;
use warnings;
...
setpwent();
while (my #list = getpwent()) {
my ($user, $uid) = #list[2, 3];
push #users, $user
if $ui >= 1001;
}
endpwent();
print scrolling_list(
-name => 'user_list',
-values => \#users,
-size => 15,
);

As documented in the CGI perldoc, the thing you pass with -values should be a list ref, but you've created a list ref to a list of hashes (due to your use of curly braces above). Here's a fixed version:
setpwent();
while (#list = getpwent())
{
($LOGIN,$PASSWORD,$UID,$GID,$QUOTA,$COMMENT,$GECOS,$HOMEDIR,$SHELL) = #list[0,1,2,3,4,5,6,7,8];
if( $UID >= 1001 )
{
push #users, $LOGIN;
}
}
endpwent();
print scrolling_list(-name=>'user_list',
-values=>\#users,
-size=>15);

Related

Converting and printing array of hashes - Perl

I really dont know how to do it so I ended up here.
I want to convert this input:
my #sack_files_1 = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
'u5env/1_2_3/parent.dx_environment',
'u5env/1_2_3/doc/types.dat',
);
To this:
my $sack_tree_1 = {
'mgenv' => {
'1_2_3' => [ 'parent.dx_environment', 'doc/types.dat' ],
},
'u5env' => {
'1_2_3' => [ 'parent.dx_environment', 'doc/types.dat' ],
}
};
Something like this should do the trick:
use strict;
use warnings;
use Data::Dumper;
my #sack_files_1 = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
'u5env/1_2_3/parent.dx_environment',
'u5env/1_2_3/doc/types.dat',
);
my %sack_tree_1;
foreach (#sack_files_1) {
my ( $env, $number, #everything_else ) = split('/');
push( #{ $sack_tree_1{$env}{$number} }, join( "/", #everything_else ) );
}
print Dumper \%sack_tree_1
This will do as you ask. It uses File::Spec::Functions to split each path into its components.
The first two elements of the hash are used directly as hash keys, relying on autovivication to create the necessary hash elements.
A simple push to an implied array reference also autovivifies the lowest-level hash element.
I have used Data::Dump to display the resulting hash. It is not part of the core Perl installation and you may need to install it, but it is much superior to Data::Dumper.
use strict;
use warnings;
use File::Spec::Functions qw/ splitdir catfile /;
my #sack_files_1 = (
'mgenv/1_2_3/parent.dx_environment',
'mgenv/1_2_3/doc/types.dat',
'u5env/1_2_3/parent.dx_environment',
'u5env/1_2_3/doc/types.dat',
);
my %paths;
for my $path (#sack_files_1) {
my ($p1, $p2, #path) = splitdir $path;
push #{ $paths{$p1}{$p2} }, catfile #path;
}
use Data::Dump;
dd \%paths;
output
{
mgenv => { "1_2_3" => ["parent.dx_environment", "doc\\types.dat"] },
u5env => { "1_2_3" => ["parent.dx_environment", "doc\\types.dat"] },
}
my $sack_tree_1 = {};
foreach my $data (#sack_files_1) {
my #path = split '/', $data;
my ($file,$last_part) = pop #path, pop #path; # get the file name and last part of the path
my $hash_part = $sack_tree_1;
foreach my $path (#path) { # For every element in the remaining part of the path
$hash_part->{$path} //= {}; # Make sure we have a hash ref to play with
$hash_part = $hash_part->{$path} # Move down the hash past the current path element
}
$hash_part->{$last_part} = $file; # Add the file name to the last part of the path
}
This handles all path lengths of 2 or more

Trying to figure out how to push specific links contained in each link of separate list of links into an array

GENERAL IDEA
Here is a snippet of what I'm working with:
my $url_temp;
my $page_temp;
my $p_temp;
my #temp_stuff;
my #collector;
foreach (#blarg_links) {
$url_temp = $_;
$page_temp = get( $url_temp ) or die $!;
$p_temp = HTML::TreeBuilder->new_from_content( $page_temp );
#temp_stuff = $p_temp->look_down(
_tag => 'foo',
class => 'bar'
);
foreach (#temp_stuff) {
push(#collector, "http://www.foobar.sx" . $1) if $_->as_HTML =~ m/href="(.*?)"/;
};
};
Hopefully it is clear that what I'm hopelessly trying to do is push the link endings found in each of a list of links into an array called #temp_stuff. So the first link in #blarg_links, when visited, has greater than or equal to 1 foo tag with an associated bar class that when acted on by as_HTML will match something I want in the href equality to then pump into an array of links which have the data I'm really after... Does that make sense?
ACTUAL DATA
my $url2 = 'http://www.chemistry.ucla.edu/calendar-node-field-date/year';
my $page2 = get( $url2 ) or die $!;
my $p2 = HTML::TreeBuilder->new_from_content( $page2 );
my #stuff2 = $p2->look_down(
_tag => 'div',
class => 'year mini-day-on'
);
my #chem_links;
foreach (#stuff2) {
push(#chem_links, $1) if $_->as_HTML =~ m/(http:\/\/www\.chemistry\.ucla\.edu\/calendar-node-field-date\/day\/[0-9]{4}-[0-9]{2}-[0-9]{2})/;
};
my $url_temp;
my $page_temp;
my $p_temp;
my #temp_stuff;
my #collector;
foreach (#chem_links) {
$url_temp = $_;
$page_temp = get( $url_temp ) or die $!;
$p_temp = HTML::TreeBuilder->new_from_content( $page_temp );
#temp_stuff = $p_temp->look_down(
_tag => 'span',
class => 'field-content'
);
};
foreach (#temp_stuff) {
push(#collector, "http://www.chemistry.ucla.edu" . $1) if $_->as_HTML =~ m/href="(.*?)"/;
};
n.b. - I want to use HTML::TreeBuilder. I'm aware of alternatives.
This is a rough attempt at what I think you want.
It fetches all the links on the first page and visits each of them in turn, printing the link in each <span class="field-content"> element.
use strict;
use warnings;
use 5.010;
use HTML::TreeBuilder;
STDOUT->autoflush;
my $url = 'http://www.chemistry.ucla.edu/calendar-node-field-date/year';
my $tree = HTML::TreeBuilder->new_from_url($url);
my #chem_links;
for my $div ( $tree->look_down( _tag => 'div', class => qr{\bmini-day-on\b} ) ) {
my ($anchor)= $div->look_down(_tag => 'a', href => qr{http://www\.chemistry\.ucla\.edu});
push #chem_links, $anchor->attr('href');
};
my #collector;
for my $url (#chem_links) {
say $url;
my $tree = HTML::TreeBuilder->new_from_url($url);
my #seminars;
for my $span ( $tree->look_down( _tag => 'span', class => 'field-content' ) ) {
my ($anchor) = $span->look_down(_tag => 'a', href => qr{/});
push #seminars, 'http://www.chemistry.ucla.edu'.$anchor->attr('href');
}
say " $_" for #seminars;
say '';
push #collector, #seminars;
};
For a more modern framework for parsing webpages, I would suggest you take a look at Mojo::UserAgent and Mojo::DOM. Instead of having to manually march through each section of your html tree, you can use the power of css selectors to zero in on the specific data that you want. There's a nice 8 minute introductory video on the framework at Mojocast Episode 5.
# Parses the UCLA Chemistry Calendar and displays all seminar links
use strict;
use warnings;
use Mojo::UserAgent;
use URI;
my $url = 'http://www.chemistry.ucla.edu/calendar-node-field-date/year';
my $ua = Mojo::UserAgent->new;
my $dom = $ua->get($url)->res->dom;
for my $dayhref ($dom->find('div.mini-day-on > a[href*="/day/"]')->attr('href')->each) {
my $dayurl = URI->new($dayhref)->abs($url);
print $dayurl, "\n";
my $daydom = $ua->get($dayurl->as_string)->res->dom;
for my $seminarhref ($daydom->find('span.field-content > a[href]')->attr('href')->each) {
my $seminarurl = URI->new($seminarhref)->abs($dayurl);
print " $seminarurl\n";
}
print "\n";
}
Output is identical to that of Borodin's solution using HTML::TreeBuilder:
http://www.chemistry.ucla.edu/calendar-node-field-date/day/2014-01-06
http://www.chemistry.ucla.edu/seminars/nano-rheology-enzymes
http://www.chemistry.ucla.edu/calendar-node-field-date/day/2014-01-09
http://www.chemistry.ucla.edu/seminars/imaging-approach-biology-disease-through-chemistry
http://www.chemistry.ucla.edu/calendar-node-field-date/day/2014-01-10
http://www.chemistry.ucla.edu/seminars/arginine-methylation-%E2%80%93-substrates-binders-function
http://www.chemistry.ucla.edu/seminars/special-inorganic-chemistry-seminar
http://www.chemistry.ucla.edu/calendar-node-field-date/day/2014-01-13
http://www.chemistry.ucla.edu/events/robert-l-scott-lecture-0
...

Perl array hash print

I'm a novice. I've got this..
%categories = (
'Announcements' => ['Auctions', 'Bands-Seeking-Members', 'Boutiques', 'Charity'],
'Appliances' => ['Dishwashers', 'Fireplaces/Wood-Burning-Stoves', 'Microwaves'],
'Auto-Parts-and-Accessories' => ['Auto-Accessories', 'Car-Audio-and-Video],
'Baby' => ['Baby-Clothing', 'Backpacks-and-Carriers', 'Changing', 'Cribs-and-Playpens'],
'Books-and-Media' => ['Blu-ray-Discs', 'Books:-Children'],
'Clothing-and-Apparel' => ['Boys-Clothing', 'Boys-Shoes', 'Costumes']
);
I need it to print this..
• Announcements
-Auctions
-Bands-Seeking-Members
-Boutiques
-Charity
•Appliances etc
This is the code so far..
while( my ($cat,$subcat) = each %categories) {
print qq~• $cat
~;
print qq~- $subcat
~;
}
It's printing this..
• Clothing-and-Apparel
- ARRAY(0x1e959c0)
• Announcements
- ARRAY(0x1e95590)
• Auto-Parts-and-Accessories
- ARRAY(0x1e95740)
I believe the following should do what you want:
while(my ($cat, $subcats) = each %categories) {
print "• $cat\n";
foreach my $subcat (#$subcats) {
print "- $subcat\n";
}
}
I have added a for loop to iterate over the contents of your subcategory arrays.
If you need the categories and subcategories sorted alphabetically, try this:
foreach my $cat (sort keys %categories) {
print "• $cat\n";
my $subcategories = $categories{$cat};
foreach my $subcat (sort #$subcategories) {
print "- $subcat\n";
}
}

How do I pass all elements of "array of hashes" into function as an array

How do I pass a element of "array of hashes" into function as an array?
say for instance I wanted to pass all $link->{text} as an array into the sort() function.
#!/usr/bin/perl
use strict; use warnings;
my $field = <<EOS;
Baboon
Antelope
dog
cat
EOS
#/ this comment is to unconfuse the SO syntax highlighter.
my #array_of_links;
while ($field =~ m{<a.*?href="(.*?)".*?>(.*?)</a>}g) {
push #array_of_links, { url => $1, text => $2 };
}
for my $link (#array_of_links) {
print qq("$link->{text}" goes to -> "$link->{url}"\n);
}
If you want to sort your links by text,
my #sorted_links = sort { $a->{text} cmp $b->{text} } #array_of_links;
If you actually just want to get and sort the text,
my #text = sort map $_->{text}, #array_of_links;
Better to err on the side of caution and use an HTML parser to parse HTML:
use strict; use warnings;
use HTML::TokeParser::Simple;
my $field = <<EOS;
Baboon
Antelope
dog
cat
EOS
my $parser = HTML::TokeParser::Simple->new(string => $field);
my #urls;
while ( my $tag = $parser->get_tag ) {
next unless $tag->is_start_tag('a');
next unless defined(my $url = $tag->get_attr('href'));
my $text = $parser->get_text('/a');
push #urls, { url => $url, text => $text };
}
#urls = sort {
$a->{text} cmp $b->{text} ||
$a->{url} cmp $b->{url}
} #urls;
use YAML;
print Dump \#urls;

Search for hash in an array by value

I have a function which extracts Excel data into an array of hashes like so:
sub set_exceldata {
my $excel_file_or = '.\Excel\ORDERS.csv';
if (-e $excel_file_or) {
open (EXCEL_OR, $excel_file_or) || die("\n can't open $excel_file_or: $!\n");
while () {
chomp;
my ( $id, $date, $product, $batchid, $address, $cost ) = split ",";
my %a = ( id => $id
, date => $date
, product => $product
, batchid => $batchid
, address => $address
, cost => $cost
);
push ( #array_data_or, \%a );
}
close EXCEL_OR;
}
}
Populating the array of hashes is fine. However, the difficult part is searching for a particular item (hash) in the array. I can't seem to locate items that might have an id or 21, or a batchid of 15, or a cost > $20 etc.
How would I go about implementing such a search facility?
Thanks to all,
With the power of grep
my #matching_items = grep {
$_->{id} == 21
} #array_data_or;
If you know there will be only one item returned you can just do this:
my ($item) = grep {
$_->{id} == 21
} #array_data_or;
(Untested, and I haven't written one of these in a while, but this should work)
If you're sure that the search always returns only one occurence or if you're interested in only the first match then you could use the 'first' subroutine found in List::Util
use List::Util;
my %matching_hash = %{ first { $_->{id} == 21 } #array_data_or };
I enclosed the subroutine call in the %{ } block to ensure that the RHS evaluates to a hash.

Resources