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

Greetings, all. I inherited some Perl code. But before I go too far with it, I wanted to get better familiar with it.

It's a Perl application. But has a bit of JavaScript included. The following is a JavasScript function. But given that Perl, and JavaScript are so similar, in so many ways. I was hoping you all might indulge me.

'function' : function (fieldObj,option) { var v = fieldObj.value = fieldObj.value.toString().replace(/\s/g,'').toUpperCase(); if (!v) { return false; } var bm = v.match(/^JM-\d+-\S{4}[1-9Zz]{6}$/); if ( v.match(/^JM-\d+-\S{4}[1-9Zz]{6}$/) ) { return true; } var map = {}; var dv = 0; $w('A B C D E F G H J K L M N P Q R T U V W Y 3 4 6 7 8 9').each( function(d){ map[d] = dv++; } ); var chars = v.toArray().reverse(); var check = chars.shift(); chars = chars.findAll(function(d){ return map[d] != null }); if (chars.length != 19) { return false; } var totalVal = 0; var flip = 1; chars.each(function(c){ var posVal = map[c]; if ( !(flip = !flip) ) { posVal *=2 } while(posVal){ totalVal = totalVal + (posVal % 10); posVal = Math.floor(posVal/10); } }); return ( check == ( 10 - totalVal % 10 ) % 10 ); }

It's a form field validation function, and requires certain input. But I'm afraid it's a bit over my head, and was hoping one of the brighter Monks here, might help me better understand it.

It seems quite clever, but either my math, or my code tracing isn't quite up to snuff, on this one.

Thank you for all your time, and consideration.

--Chris

¡λɐp ʇɑəɹ⅁ ɐ əʌɐɥ puɐ ʻꜱdləɥ ꜱᴉɥʇ ədoH

  • Comment on I'm struggling with a function, and hoping for some insight -- not strictly Perl
  • Download Code

Replies are listed 'Best First'.
Re: I'm struggling with a function, and hoping for some insight -- not strictly Perl
by smls (Friar) on May 18, 2014 at 11:20 UTC

    Your function considers an input string as valid, if (after sanitizing) it either matches a certain regex, or passes a certain check digit algorithm.

    There are many standardized check digit algorithms in use; if you need to find out which one you're dealing with here, maybe this Wikipedia page can help.
    Edit: As it turns out, it's the Luhn algorithm.

    Anyway, here is a more or less literal translation of your code to to Perl with many explanatory comments (keeping in mind that I had to make an educated guess about the non-standard function discussed in a separate answer above):

    #!/usr/bin/perl use strict; use warnings; use feature 'say'; sub validate_serial_number { my $v = shift; $v =~ s/\s//g; # remove whitespace $v = uc $v; # convert to uppercase # 1) Try regex: return 0 if !length $v; return 1 if $v =~ /^JM-\d+-\S{4}[1-9Zz]{6}$/; # 2) Initialize the %map hash with (A=>0, B=>1, C=>2, ...): my $dv = 0; my %map = map { $_ => $dv++ } qw(A B C D E F G H J K L M N P Q R T U V W Y 3 4 6 7 8 9); # 3) Extract significant character data, for example: # 'FOOBAR123ABCDE' -> chars: qw(D C B A 3 R A B F) # check: 'E' my @chars = reverse split '', $v; my $check = shift @chars; @chars = grep { exists $map{$_} } @chars; # 4) Sanity check: return 0 if @chars != 9; # 5) Perform check digit algorithm: my $totalVal = 0; # Loop over the reversed array of input characters: for my $i (0..$#chars) { # Map the character to a unique numeric value: my $posVal = $map{$chars[$i]}; # Double the value, but only for every second character: $posVal *= 2 if !($i % 2); # Add the sum of the digits of $posVal to $totalVal: while($posVal){ $totalVal += $posVal % 10; $posVal = int($posVal / 10); } } # Substract the last digit of $totalVal from 10, and take the # last digit of *that* to get the check digit: return ( $check eq ( 10 - $totalVal % 10 ) % 10 ); } for ('Foo abr 123 ABCD6', 'Foo bar 123 ABCD6') { say "'$_' is " . (validate_serial_number($_) ? "valid" : "invalid"); }

    Output:

    'Foo abr 123 ABCD6' is invalid 'Foo bar 123 ABCD6' is valid
      Hello, smls, and thank you VERY much for an over-the-top reply!
      The Points Fairy thought so too. :)

      In all fairness to anyone looking to respond; I should have probably noted in the OP, that I think my difficulty in following the logic, or flow, seemed to be with my apparent lack of knowledge with the map function. The use of split in the code here, might also be a factor. But, where split is concerned, may well be related to my lack of experience with map, or because this is in JavaScript, not Perl.

      Thanks again, smls, for such an informative reply!

      --Chris

      ¡λɐp ʇɑəɹ⅁ ɐ əʌɐɥ puɐ ʻꜱdləɥ ꜱᴉɥʇ ədoH

Re: I'm struggling with a function, and hoping for some insight -- not strictly Perl
by smls (Friar) on May 18, 2014 at 09:14 UTC
    $w('A B C D E F G H J K L M N P Q R T U V W Y 3 4 6 7 8 9').each( function(d){ map[d] = dv++; } );

    ^^ This looks very similar to jQuery syntax for looping over DOM elements. For example you could loop over all "div" elements of a web page, and do something with each of them, using this jQuery syntax:

    $( "div" ).each(function(i) { // in here, 'this' can be used to refer to the current DOM // element, whereas 'i' contains the loop index });

    $ is not a sigil, it is the name of a function defined by the jQuery library.

    In your code, a similar-behaving function called $w is used. It will probably help your reverse-engineering effort if you could find out where that function is defined, and what exactly it does.
    Update: Looks like it may be simply doing what Perl's qw(...) quote-like operator does...

    Edit: Moved Perl translation and high-level discussion to a separate answer.

Re: I'm struggling with a function, and hoping for some insight -- not strictly Perl
by boftx (Deacon) on May 18, 2014 at 08:15 UTC

    What part of it is giving you trouble? I don't quite understand all of it myself, but I can see right off the top that the missing letters from the $w array(?) correspond to the the missing numerals in that 'I', 'O', 'S' and 'Z' could be confused for the numerals '1', '0', '5' and '2'.

    This leads me to think this is some form of CAPTCHA field.

    It helps to remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
      Hello, boftx! How's the weather down in TX?
      Hey. Your answer makes me feel better, that I can't quite follow it. :)
      Good guess. But not quite. It's actually a number validation; which you pretty much guessed. But it's not for CAPTCHA, it's a SERIAL number. :) No. I'm NOT attempting to thwart some PAY-FOR application. I found a link to an application I thought I'd (and others might) like to use. Problem was, the site, and application had apparently been abandoned. So, after much detective work. I was able to get ahold of the author, and inquired as to what it would take to continue development on the application. He said I could have it, but on the condition that it was AS-IS, and WITHOUT ANY support from him.
      Apparently he'd used it for income, but got a real mucky-muck job, and position in a big mucky-muck firm. So had no more interest, or time for it.
      Well I accepted his offer, and have been working with it since.

      I simply nuked the serial function(s). But they seemed so clever (to me, anyway) that I thought it'd be well worth the effort to understand it better.

      So here I am, asking/begging. :)

      Thanks for the reply, boftx!

      --Chris

      ¡λɐp ʇɑəɹ⅁ ɐ əʌɐɥ puɐ ʻꜱdləɥ ꜱᴉɥʇ ədoH

        More on topic, I'm not a big fan of having validation like that on the client side (assuming it is client-side since it is js.) I'd much rather see it done server-side through an AJAX call if you don't want to cycle through a page reload. Can you move it there and write it in Perl? (For those who are picky, "Perl" is the language, "perl" is the program.)

        It helps to remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.

        Sorry I couldn't help more, maybe a couple more shots of Scotch will help me.

        BTW, I'm back in Vegas. Apparently $work thinks I'm good enough that they want me to be happy and will let me work remote from home. :)

        It helps to remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
Re: I'm struggling with a function, and hoping for some insight -- not strictly Perl
by Anonymous Monk on May 18, 2014 at 10:33 UTC

    might help me better understand it.

    For what purpose? I would study the documentation first :)

    It seems quite clever

    Also seems like junk not coping with scoping very well and poor variable names ... and incomplete, no wonder understanding it is hard

    JSHint, a JavaScript Code Quality Tool

Re: I'm struggling with a function, and hoping for some insight -- not strictly Perl
by Anonymous Monk on May 18, 2014 at 15:07 UTC
    JavaScript logic is always hard to follow because it usually doesn't have comments or many blank lines ... even when it isn't "min-ized." Sometimes the logic is obfuscatory, maybe because someone thought it was faster and maybe it was. If there are particular portions of the logic that you're looking at -- say, regexes, or "each()" -- these would certainly qualify as legitimate Perl-ish questions and we can help. Target your questions . . .