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

<Readme> Perl newbie needs help with a hash. Recent Active Dir site has enabled smart card login & need to do a comparison of two arrays to get username/smartcard id to match up. Code looks like this</readme>
@getuser = `dsquery user –name * -o samid –limit 1000`; foreach (@getuser) { $a = $_; chomp $a; $b = (split (/”/, $a))[1]; } @getcert = `dsquery usern –name * -o upn –limit 1000`; foreach (@getcert) { $c = $_; chomp $c; $d = (split (/”/, $c))[1]; $e = (split (/@/, $d))[0]; }
<Readme> Output from array 1(username) is as follows: smithj jonesa johnsonw Output from array 2(smartcard id) is as follows: 1234567 8901234 5678901 Output from both arrays are in same order, so i just need a one for one association (eg, output1 from first array will always = output2 from array2, and so forth), that is output like: smithj = 1234567 jonesa = 8901234 johnsonw = 5678901 Not sure how to associate this - would appreciate any assists</readme>

Replies are listed 'Best First'.
Re: Newbie Needs Help with Hash
by kyle (Abbot) on Sep 13, 2007 at 03:04 UTC

    First of all, it's a bad idea to use the variable names $a and $b in your code because they're special variables used by sort.

    Secondly, you can write this:

    foreach (@getcert) { $c = $_;

    ...as this:

    foreach my $c (@getcert) {

    To get your arrays into a hash, I'd use List::MoreUtils.

    use List::MoreUtils qw( mesh ); my @users = qw( alice bob charlie ); my @ids = qw( 123 456 789 ); my %id_of = mesh @users, @ids; # now $id_of{ 'alice' } == 123

    If you don't want to install List::MoreUtils, you could just paste in the source for mesh:

    sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\ +@\@\@) { my $max = -1; $max < $#$_ && ($max = $#$_) for @_; map { my $ix = $_; map $_->[$ix], @_; } 0..$max; }

    Lastly, if you haven't already, consider "use strict;use warnings;".

Re: Newbie Needs Help with Hash
by almut (Canon) on Sep 13, 2007 at 03:37 UTC

    You could also use a hash slice to associate the two arrays:

    my @users = qw( alice bob charlie ); my @ids = qw( 123 456 789 ); my %id_of; @id_of{@users} = @ids; # hash slice use Data::Dumper; print Dumper(\%id_of);

    Output

    $VAR1 = { 'alice' => '123', 'charlie' => '789', 'bob' => '456' };
Re: Newbie Needs Help with Hash
by mayaTheCat (Scribe) on Sep 13, 2007 at 06:44 UTC
    Although it is not your primary concern in your question, I would like to comment on the way you extract user name and smart card id from the strings.

    You can utilize regexes for the task. The following is a recommendation.

    use warnings; use strict; my $user_name_regex = qr/ ~ # match the first ~ ([^~]+) # capture the characters as long as it is not a ~ /x; my $smartcard_id_regex = qr/ ~ # match the first ~ ([^~@]+) # capture the characters as long as it is not a ~ or @ /x; @getuser = `dsquery user –name * -o samid –limit 1000`; my @user_names = map { chomp; $_ =~ $user_name_regex; # match and capture user name $1 # return the capture; } @getuser; @getcert = `dsquery usern –name * -o upn –limit 1000`; my @smartcard_ids = map { chomp; $_ =~ $smartcard_id_regex; # match and capture smartcard id $1; # return the capture } @getcert;
    Oguz

    ---------------------------------
    life is ... $mutation = sub { $_[0] =~ s/(.)/rand()<0.1?1-$1:$1/ge };

Re: Newbie Needs Help with Hash
by hangon (Deacon) on Sep 13, 2007 at 14:34 UTC

    Here is a modified version of your code that builds a hash allowing lookup either by user or smartcard id. This is assuming $b = user and $e = smartcard id (your variable names don't provide a clue). Since the arrays are 1:1, the for loop is used to generate a common index into both arrays. For your own benefit (code maintenance), please consider:

    • Not using single character variable names. Some have special meaning.
    • Renaming your variables to something more meaningful.
    • Using lexically scoped (my) variables.
    use strict; use warnings; my %id_lookup; my @getuser = `dsquery user -name * -o samid -limit 1000`; my @getcert = `dsquery usern -name * -o upn -limit 1000`; for (0 .. scalar @getuser -1){ my $a = $getuser[$_]; chomp $a; my $b = (split (/"/, $a))[1]; my $c = $getcert[$_]; chomp $c; my $d = (split (/"/, $c))[1]; my $e = (split (/@/, $d))[0]; $id_lookup{$b} = $e; # this line adds reverse lookup, if desired $id_lookup{$e} = $b; }