Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

How do you sort keys of a hash in descending order and print 3 per line?

by MikeyG (Novice)
on Mar 16, 2016 at 00:24 UTC ( [id://1157873]=perlquestion: print w/replies, xml ) Need Help??

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

Below is my entire script, the problem I have is with Choice number 4.

#! /usr/bin/perl -W use strict; use 5.010; my %accounts = (tom => "BigApple"); my %types = (tom => "admin"); my $choice; my ($userID, $passwd, $type); my @userID = $userID; my $menu = <<MENU; System Administration Tasks 1- Create a new account 2- Delete an account 3- Change an account password 4- List users in alphabetical order 5- Exit the system MENU while (1) { print $menu, "\n"; print "Make your choice: "; chomp($choice = <STDIN>); if ($choice == 1){ print "Enter your userID: "; chomp ($userID = <STDIN>); print "Enter your password: "; chomp ($passwd = <STDIN>); if (exists $accounts{$userID}) { print "Duplicate user id, try again later" }else { print "Enter the account type, admin (or regular): "; chomp ($type = <STDIN>); $accounts{$userID} = $passwd; $types{$userID} = $type; print "Account was successfully created \n"; } } elsif ($choice == 4){ # sort the users in descending order foreach $userID (reverse sort keys %accounts) { # print out login names 3 per line @keys = keys %accounts; for (my $counter = 1; $counter <= @keys; $counter++) { print "$userID\t"; print "\n" if $counter % 3 == 0; } } } else { print "Invalid choice, TRY AGAIN!\n"; } }

Original code restored below by GrandFather

#! /usr/bin/perl -W use strict; use 5.010; sub heading; # A script that allows a user to perform system administration tasks # Programmer name: Michael A. Gregor # Course: COSC 146 # Lab#: 7 # Due Date: 3/10/16 # lab7.pl my %accounts = (tom => "BigApple"); my %types = (tom => "admin"); my $choice; my ($userID, $passwd, $type); my @userID = $userID; # call the function named 'heading' to display the output heading heading; my $menu = <<MENU; System Administration Tasks 1- Create a new account 2- Delete an account 3- Change an account password 4- List users in alphabetical order 5- Exit the system MENU while (1) { # display the menu print $menu, "\n"; # get the user choice and fullfil the request print "Make your choice: "; chomp($choice = <STDIN>); if ($choice == 1){ #print "a\n"; # replace the print-statements with your own code !! +!!!!!!!!!!!!!!! # get user id and password print "Enter your userID: "; chomp ($userID = <STDIN>); print "Enter your password: "; chomp ($passwd = <STDIN>); # is there an account by that name already? if so, display a messa +ge and then start the next loop iteration if (exists $accounts{$userID}) { print "Duplicate user id, try again later" } # no account by that name, ask the user for the account type else { print "Enter the account type, admin (or regular): "; chomp ($type = <STDIN>); # create the account and then print a message $accounts{$userID} = $passwd; $types{$userID} = $type; print "Account was successfully created \n"; } } elsif ($choice == 2){ # get user id and password print "Enter your userID: "; chomp ($userID = <STDIN>); print "Enter your password: "; chomp ($passwd = <STDIN>); # authenticate the user account. If it is a valid account with + admin privileges, go ahead and delete the # account. If the account is invalid or has no admin privieges +, display an appropriate message in each case if (authenticate($userID, $passwd, %accounts) == 1) { if ($type eq 'admin') { delete $accounts{$userID}; print "Account was deleted successfully"; } else { print "Can't delete, Contact the system administrator" +; } } } elsif ($choice == 3){ # get user id and password print "Enter your userID: "; chomp ($userID = <STDIN>); print "Enter your password: "; chomp ($passwd = <STDIN>); # authenticate the user account. If it is a valid account, ask + the user for the new password. # have the user re-enter the new password again and confirm th +at they are the same. If so, change # the password. In any case, print a appropriate message if (authenticate($userID, $passwd, %accounts) == 1) { print "Enter a new password: "; chomp (my $passwd = <STDIN>); print "re-enter the password again: "; chomp (my $npass1 = <STDIN>); if ($passwd eq $npass1) { $passwd = $npass1; $accounts{$userID} = $passwd; print "password was changed successfully"; } else { print "password was not changed!"; } } } elsif ($choice == 4){ # sort the users in descending order foreach $userID (reverse sort keys %accounts) { # print out login names 3 per line @keys = keys %accounts; for (my $counter = 1; $counter <= @keys; $counter++) { print "$userID\t"; print "\n" if $counter % 3 == 0; } } } elsif ($choice == 5){ # print a message and terminate the script print "Good bye! \n\n"; exit; } else { print "Invalid choice, TRY AGAIN!\n"; } } # write a function definition for the function named 'heading'. This f +unction prints out the output heading sub heading { print "\n"; print "Programmer name: Michael A. Gregor \n"; print "Course: COSC 146 \n"; print "Lab#: 7 \n"; print "Due Date: 3/10/16 \n"; } # authenticate a user. return 1 if the user id and password combinatio +n is good. Otherwise return 0; sub authenticate { my $id = shift; my $passwd = shift; my %accounts = @_; if (exists $accounts{$id} && $accounts{$id} eq $passwd){ return 1; } else { return 0; } }

Replies are listed 'Best First'.
Re: How do you sort keys of a hash in descending order and print 3 per line?
by bangor (Monk) on Mar 16, 2016 at 01:27 UTC
    I think you are trying too hard :)
    my %accounts = ( tom => "BigApple", tom2 => "BigApple2", tom3 => "BigApple3", tom4 => "BigApple4", tom5 => "BigApple5", tom6 => "BigApple6", tom7 => "BigApple7", ); # sort the users in descending order my $counter = 1; for my $userID (reverse sort keys %accounts) { print "$userID\t"; print "\n" if $counter++ % 3 == 0; } tom7 tom6 tom5 tom4 tom3 tom2 tom

      You can avoid mucking about with counters and stuff by making the implicit list in the for loop an explicit array then splice chunks off it:

      #!/usr/bin/perl use warnings; use strict; my %accounts = ( tom => "BigApple", tom2 => "BigApple2", tom3 => "BigApple3", tom4 => "BigApple4", tom5 => "BigApple5", tom6 => "BigApple6", tom7 => "BigApple7", ); my @keys = reverse sort keys %accounts; print join ("\t", @$_), "\n" while @$_ = splice @keys, 0, 3;

      Prints:

      tom7 tom6 tom5 tom4 tom3 tom2 tom
      Premature optimization is the root of all job security

      I received this message: Argument "t" isn't numeric le (<=) at c:\users\michael\desktop\lab7-startup.pl line 119, <STDIN> line 17. Any idea why?

        I received this message: Argument "t" isn't numeric le (<=) ... Any idea why?

        Because you've mis-copy/pasted the code. I can copy/paste the exact code posted by bangor above and run it with warnings and strict enabled, and no complaint from Perl.


        Give a man a fish:  <%-{-{-{-<

        He's a monk, not a clairvoyant!

        Please post the relevant line of code that the error is pointing you to :-)


        The way forward always starts with a minimal test.
Re: How do you sort keys of a hash in descending order and print 3 per line?
by 1nickt (Canon) on Mar 16, 2016 at 01:09 UTC

    Your program doesn't compile as posted. You have an undeclared variable in there.

    Your main problem is that you have nested for loops that cause the program to print each name the number of times there are items in the list. You need to consolidate the two things you are doing in loops so that you only loop through the names once, using the same loop for your counter.

    Hope this helps!


    Edit: Updated answer
    The way forward always starts with a minimal test.
Re: How do you sort keys of a hash in descending order and print 3 per line?
by Marshall (Canon) on Mar 16, 2016 at 04:53 UTC
    Please, don't remove code after you post it.

    I made a few mods that you might find instructive. And added an undocumented option 6. Add a few accounts and see what that does!

    #!/usr/bin/perl -W use strict; use 5.010; use Data::Dumper; my $heading = <<HEAD; A script that allows a user to perform system administration tasks Programmer name: Michael A. Gregor Course: COSC 146 Lab#: 7 Due Date: 3/10/16 lab7.pl HEAD print $heading; my %passwords = (tom => "BigApple"); my %types = (tom => "admin"); my $menu = <<MENU; System Administration Tasks 1- Create a new account 2- Delete an account 3- Change an account password 4- List users in alphabetical order 5- Exit the system MENU while (print $menu) { my $choice = prompt4input ("Make your choice: "); if ($choice == 1) {createAccount(); next;} if ($choice == 2) {deleteAccount(); next;} if ($choice == 3) {changePassword(); next;} if ($choice == 4) {listUsers(); next;} if ($choice == 5) {print "Good-bye!";exit(0);} if ($choice == 6) {print Dumper (\%passwords); print Dumper (\%types); next;} print ("Bad Choice...Try Again!\n"); } sub prompt4input #returns response to a prompt string { my $prompt = shift; print $prompt; my $input = <STDIN>; $input =~ s/^\s*//; #remove leading whitespace $input =~ s/\s*$//; #remove trailing whitespace return $input; } sub createAccount { print "Creating Account...\n"; my $userID = prompt4input ("Enter your userID: "); my $password = prompt4input ("Enter your password: "); if (exists $passwords{$userID}) { print "Error: Duplicate user id: $userID "; return; #### update added } my $account_type; while ($account_type = prompt4input( "Enter the account type, admin (or regular): "), $account_type !~ /admin|regular/) { $passwords{$userID} = $password; $types{$userID} = $account_type; } print "Account for \'$userID\' was successfully created \n"; } sub deleteAccount { my $userID = prompt4input ("Enter your userID: "); my $password = prompt4input ("Enter your password: "); if ($passwords{$userID} != "$password") { print "invalid user id/password combination!\n"; return; } if ($types{$userID} != "admin") { print "$userID is not an admin...can not delete!\n"; return; } delete $passwords{$userID}; delete $types{$userID}; } sub changePassword { my $userID = prompt4input ("Enter your userID: "); my $password = prompt4input ("Enter your password: "); if ($passwords{$userID} != "$password") { print "invalid user id/password combination!\n"; return; } my $newPassword = prompt4input ("Enter new password: "); $passwords{$userID} = $newPassword; } sub listUsers { # sort the users in descending order my $counter = 1; for my $userID (reverse sort keys %passwords) { print "$userID\t"; print "\n" if $counter++ % 3 == 0; #divisible by 3 evenly } print "\n"; }
Re: How do you sort keys of a hash in descending order and print 3 per line?
by Anonymous Monk on Mar 16, 2016 at 00:58 UTC

    Below is my entire script, the problem I have is with Choice number 4.

    ok, copy/paste your program, delete everything except "choice 4" and whats needed to run that portion, it shouldn't take long, and then your question code will be less than 20 lines

    Also, you have too much copy/paste, you need more subroutines

      I cut the code down to choice 1 and choice 4. I need choice 1 because you are suppose to be add userIDs and passwords. Choice 4 is suppose to be able to print those userIDs in descending order.

        Hi MikeyG,

        I don't think the anonymonk was suggesting to edit your OP. For one thing, it's better to leave them alone for history (ie other posts may refer to text that is then removed...), and for another, it was useful to see your whole script. Also, you introduced a syntax error when cutting code out, so what's left no longer compiles, as your original script did.

        I think the anonymonk was suggesting to create a Short, Self-Contained, Compilable Example, which eliminates all but the essential code to show your problem. Mind you it should compile and run. In your case you could have shown code with just your sub for question number 4, providing the test data in a hash. Then the monks would have only had a dozen lines of code to review, and you would get help more easily. (Edit: much as in the example bangor showed you.)

        As an added bonus, creating a SSCCE is very closely related to debugging and often the process of doing it will lead you to the answer yourself.

        The way forward always starts with a minimal test.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1157873]
Approved by stevieb
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (6)
As of 2024-04-24 06:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found