SitePoint Sponsor

User Tag List

Results 1 to 13 of 13
  1. #1
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)

    Ranges revisited.

    So I have this range reduce call that was posted here on the forum

    Code php:
    function reduce_ranges($reduced, $value) {
        static $in_range, $previous;
        // Reset
        if ($in_range === NULL) {
            $in_range = FALSE;
            $previous = NULL;
        }
        // In a range
        if (isset($previous) && $value == $previous + 1) {
            if (!$in_range) {
                $in_range = TRUE;
            }
            // Update the range
            end($reduced);
            $key = key($reduced);
            $start = strtok($reduced[$key], "-");
            $reduced[$key] = $start."-".$value;
        // Not in a range
        } else {
            $reduced[] = $value;
            $in_range = FALSE;
        }
        $previous = $value;
        return $reduced;
    }

    Works fine, until alphanumeric codes are used. Starting this year, the State of Tennessee decided to do just that with license plate decals, so I have to rewrite this method to tolerate numbers with alpha prefixes.

    For example, finding a range of: A100-A200

    However if the letter does change it needs to start a new range (obviously).

    I'm guessing I'll need to use a regex on these, and that's one area of code I'm not strong with. If it helps, for now, if there is a letter present it will be the first character of the string.

  2. #2
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,396
    Mentioned
    54 Post(s)
    Tagged
    0 Thread(s)
    Which part in particular, if any, are you having trouble with? I don't know about other folks here, but I'd rather help you write your code than cook up some "solution" that you might not want.
    Salathe
    Software Developer and PHP Manual Author.

  3. #3
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Hi Michael,

    I searched the PHP forums in hope of seeing the input for this function so I could better understand what data should output, but could not find its' original reference. Do you mind showing a small example in the context that it is used?

    Steve
    ictus==""

  4. #4
    SitePoint Mentor bronze trophy
    John_Betong's Avatar
    Join Date
    Aug 2005
    Location
    City of Angels
    Posts
    1,577
    Mentioned
    62 Post(s)
    Tagged
    3 Thread(s)
    For those that are not familiar with the ranges in the States, can you supply all the other ranges.

  5. #5
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,396
    Mentioned
    54 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ServerStorm View Post
    I searched the PHP forums in hope of seeing the input for this function so I could better understand what data should output, but could not find its' original reference. Do you mind showing a small example in the context that it is used?
    Steve, it's a callback for array_reduce(). See http://www.sitepoint.com/forums/show...=1#post4548334
    Salathe
    Software Developer and PHP Manual Author.

  6. #6
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Thanks Salathe... saves Michael from redoing this and examples it well!

    Steve
    ictus==""

  7. #7
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)
    Up until this year Decal numbers of the state of Tennessee where simply numeric. As of this year the "numbers" may have a letter - as in "A002841200". I need to ignore that letter for constructing ranges, but if the letter changes, I need to respect that and start a new range. There is NOT a foldover from A9999 to B0000, those will be the ends of two different ranges. I should never have a range A234 - B100 or the like.

  8. #8
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Hi Michael,

    I don't know if this will help but I created this function that will split the alphanumeric string into the Letter and the number; it returns an array('number'=>10000, 'string'=>'A');

    Here is the function:
    PHP Code:
    function split_alphanumeric($string){
      
    preg_match_all('/([\d]+)/',$string$num_target); // matches just the numbers   
      
    preg_match_all('/[a-zA-Z]/'$string$str_target); // matches just the alpha character(s)
      
    $num null;
      
    $num $num_target[0][0];
      
    $str null;
      
    $str $str_target[0][0];
      
    $values = array();
      
    $values['number'] = (int)$num;
      
    $values['string'] = $str;
      return 
    $values;

    And using it:
    PHP Code:
    $string 'A10000';
    $values split_alphanumeric($string);
    echo 
    '<pre>';var_dump($values); echo "</pre>"
    Creates this:
    Code:
    array(2) {["number"]=>  int(10000)  ["string"]=>  string(1) "A" }
    It would always create an array for the start of the range and would need to be recalled for the ending of the range. Alternatively the regex could be embedded in your function.

    Steve
    ictus==""

  9. #9
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)
    My solution. Hamfisted probably, but it is working

    Code php:
    function reduce_ranges($reduced, $value) {
     
        static $in_range, $previous, $prefix;
        // Reset
        if ($in_range === NULL) {
            $in_range = FALSE;
            $previous = NULL;
            $prefix = NULL;
        }
     
        // Using the newer prefixed numbers system.
        if (!is_numeric($value)) {
        	$currentPrefix = substr($value, 0, 1);
        	$numericPiece = (int) substr($value, 1, strlen($value));
        } else {
        	$numericPiece = $value;
        }
     
        // In a range
        if (isset($previous) && $numericPiece == $previous + 1 && $currentPrefix == $prefix ) {
            if (!$in_range) {
                $in_range = TRUE;
            }
            // Update the range
            end($reduced);
            $key = key($reduced);
            $start = strtok($reduced[$key], "-");
            $reduced[$key] = $start."-".$value;
        // Not in a range
        } else {
            $reduced[] = $value;
            $in_range = FALSE;
            $prefix = $currentPrefix;
        }
        $previous = $numericPiece;
        return $reduced;
    }

    Well, it was working in test. I spent half of the day double guessing this function - turns out it busts if there are duplicate entries in the range. For my particular application that's bad - that means the prior coder did not check his inputs when he wrote that code awhile ago.

  10. #10
    Foozle Reducer ServerStorm's Avatar
    Join Date
    Feb 2005
    Location
    Burlington, Canada
    Posts
    2,699
    Mentioned
    89 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    My solution. Hamfisted probably, but it is working
    ...
    Well, it was working in test. I spent half of the day double guessing this function - turns out it busts if there are duplicate entries in the range. For my particular application that's bad - that means the prior coder did not check his inputs when he wrote that code awhile ago.
    Glad you got it working Michael. I knew having sit in this forum as long as it did that you would most likely have found a way.

    Thanks for sharing your solution.

    Too bad the previous coder did not validate the input... this is an important lesson for every php coder

    Steve
    ictus==""

  11. #11
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,396
    Mentioned
    54 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Michael Morris View Post
    I spent half of the day double guessing this function
    That's usually a sign to step back and look at the problem (and "solution") again. The reducing function from the first post was written for a (slightly) different case, for a user who appeared to just want some code—any code!—that worked and run away with it, and was written by someone who uses array_reduce() and friends very regularly.

    It deliberately wasn't the simplest approach nor did it pass the what-on-earth-does-this-do-at-a-glance test. I also tend to forget that others may be choosing to pick up code posted on the forums, expecting them to work for them too!

    Quote Originally Posted by Michael Morris View Post
    turns out it busts if there are duplicate entries in the range.
    Yep, it doesn't particularly like duplicates.




    If you're not bent on going to the trouble of using array_reduce() and a callback function, a more basic approach might be employed: a single loop over the array, building up the ranges step by step.

    PHP Code:
    function get_ranges($array) {
        
    $reduced  = array();
        
    $expected NULL;
        
    $key      0;
        foreach (
    $array as $current) {
            if (
    $current === $expected) {
                
    // Got expected next value in range
                // Update current range to show new end value
                
    $start strtok($reduced[$key], '-');
                
    $reduced[$key] = $start.'-'.$current;
            } else {
                
    // Broken sequence, start a new range
                
    $reduced[++$key] = $current;
            }
            
    // Calculate what the next value in the range should be
            
    if (ctype_digit($current)) {
                
    $expected = (string) ($current 1);
            } else {
                
    $expected $current[0] . (substr($current1) + 1);
            }
        }
        return 
    $reduced;

    Hope that makes sense. Oh and the function above will also barf on duplicates, but it's only a couple of lines extra in the right place if you still need to deal with them inside the function. I'm happy to help if that is the case.
    Salathe
    Software Developer and PHP Manual Author.

  12. #12
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    56 Post(s)
    Tagged
    0 Thread(s)
    The thing is, the solution works. Duplicates are forbidden in this data structure - so that's an error in the code I wasn't aware of created by another programmer that I now have to fix.

  13. #13
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,396
    Mentioned
    54 Post(s)
    Tagged
    0 Thread(s)
    I don't know what you want. A lollipop?
    Salathe
    Software Developer and PHP Manual Author.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •