Subject: Zip Code Sorting
From: [email-masked] (Scott Holmes)
Date: 12 Nov 1995 19:38:04 -0500
Newsgroups: comp.databases.informix

It was recently requested of me to modify a label printing program
we've had around for a long time (possibly even version 1.0).  We have
a client that does bulk mailings once or twice a year and they want to
take advantage of special rates for sorting by zip code.  I've written
a few functions that satisfy the request but I can see potential for
improvement and increased functionality.  So, if any of you would care
to play with this --- here it is.

   I've made note of some possible enhancements on the other end of
the included code.

Here is a short bit of the output:
#####
Loose Letters for 89119 to 89130  (2 pieces)
Loose Letters for 90000  (2 pieces)
Bundle (5 digits) for 90001 (52 pieces)
Bundle (5 digits) for 90002 (20 pieces)
Bundle (5 digits) for 90003 (39 pieces)
Bundle (5 digits) for 90004 (38 pieces)
Bundle (5 digits) for 90005 (30 pieces)
Bundle (5 digits) for 90006 (54 pieces)
Bundle (5 digits) for 90007 (22 pieces)
Bundle (5 digits) for 90008 (19 pieces)
Loose Letters for 90010  (5 pieces)
Bundle (5 digits) for 90011 (106 pieces)
Bundle (3 digits) for 90012 to 90014  (12 pieces)
Bundle (5 digits) for 90015 (16 pieces)
Bundle (5 digits) for 90016 (25 pieces)
Bundle (5 digits) for 90017 (14 pieces)
Bundle (5 digits) for 90018 (30 pieces)
Bundle (5 digits) for 90019 (36 pieces)
Bundle (5 digits) for 90020 (22 pieces)

Sack (5 digits) for 90021  (130 pieces)

##############

  These variables are defined statically:

  ziparr array[1000] of record
    zipcode char(5),
    zipend char(5),
    bundle_flg char(1),
    cntr smallint
  end record,
  arr_cntr smallint,
  sacrec record
    stzip char(5),
    endzip char(5),
    cntr smallint,
    sacktype char(2)
  end record,
  sack_flag char(1),
  bundle_rec record
    stzip char(5),
    endzip char(5),
    cntr smallint,
    bundletype char(2)
  end record,
  bundle_flag char(1)

  The value for zipcode is feed to this function by the part of the
program feeding the actual label report...

#############################
FUNCTION zip_counter(zipcode)
#############################
define
  zipcode char(5)

if length(zipcode) = 5 then else return end if

if arr_cntr = 0 then         # establish the first array record
  let arr_cntr = 1
  let ziparr[arr_cntr].zipcode = zipcode
  let ziparr[arr_cntr].cntr = 1
  return
end if

if zipcode = ziparr[arr_cntr].zipcode then
  let ziparr[arr_cntr].cntr = ziparr[arr_cntr].cntr + 1
else
  let arr_cntr = arr_cntr + 1
  let ziparr[arr_cntr].zipcode = zipcode
  let ziparr[arr_cntr].cntr = 1
end if
end function
############

######################
FUNCTION zip_report()
######################
define
  n smallint,
  zip3 char(3),
  tmprec record
    snum integer,
    start_zip char(5),
    end_zip char(5),
    cntr smallint,
    sack_type char(2)
  end record

create temp table zipsacks
  (
  snum serial,
  start_zip char(5),
  end_zip char(5),
  cntr smallint,
  sack_type char(2)  # 5S, 3S, 5B, 3B, LL, MS
  )

for n = 1 to arr_cntr
  if ziparr[n].cntr >= 125 then  # 5 digit sack all by itself ...
    # Need to test for pre-existing (unfinished sacks) ...
    if ziparr[n].zipcode[1,3] <> sacrec.stzip[1,3] then
      call test_sack()
    end if
    insert into zipsacks values (0,ziparr[n].zipcode, "", ziparr[n].cntr, "5S")
    continue for
  end if

  if ziparr[n].cntr >= 10 then # 5 digit bundle ...
    # need to test for pre-existing bundles ...
    call test_bundle()
    call build_sack(ziparr[n].zipcode, ziparr[n].cntr) returning sack_flag
    if sack_flag = "Y" then    # sack needs to be created (record doesn't fit)
      insert into zipsacks values
        (0,sacrec.stzip, sacrec.endzip, sacrec.cntr, sacrec.sacktype)
      initialize sacrec.* to null
      call build_sack(ziparr[n].zipcode, ziparr[n].cntr) returning sack_flag
    end if
    insert into zipsacks values (0, ziparr[n].zipcode, "", ziparr[n].cntr, "5B")
    initialize bundle_rec.* to null
    continue for
  end if

  call build_bundle(ziparr[n].zipcode, ziparr[n].cntr) returning bundle_flag
  if bundle_flag = "Y" then  # bundle needs to be created (record doesn't fit)
    insert into zipsacks values
      (0, bundle_rec.stzip, bundle_rec.endzip, bundle_rec.cntr,
       bundle_rec.bundletype)
    if bundle_rec.bundletype = "LL" then
    else
      call build_sack(bundle_rec.stzip, bundle_rec.cntr) returning sack_flag
      if sack_flag = "Y" then    # sack needs to be created (record doesn't fit)
        insert into zipsacks values
          (0, sacrec.stzip, bundle_rec.endzip, sacrec.cntr, sacrec.sacktype)
        initialize sacrec.* to null
        call build_sack(bundle_rec.stzip, bundle_rec.cntr) returning sack_flag
      end if
    end if
    initialize bundle_rec.* to null
    call build_bundle(ziparr[n].zipcode, ziparr[n].cntr) returning bundle_flag
  end if
end for
call test_sack()

start report zipreport to "zipcount.out"
declare sack_curs cursor for select * from zipsacks
  order by zipsacks.snum
open sack_curs
while true
  fetch sack_curs into tmprec.*
  if sqlca.sqlcode then exit while end if
  case
    when tmprec.sack_type = "5S"
      call five_sack(tmprec.start_zip, tmprec.cntr)
    when tmprec.sack_type = "5B"
      call five_bundle(tmprec.start_zip, tmprec.cntr)
    when tmprec.sack_type = "3S"
      call three_sack(tmprec.start_zip, tmprec.end_zip, tmprec.cntr)
    when tmprec.sack_type = "3B"
      call three_bundle(tmprec.start_zip, tmprec.end_zip, tmprec.cntr)
    when tmprec.sack_type = "MS"
      call misc_sack(tmprec.start_zip, tmprec.end_zip, tmprec.cntr)
    when tmprec.sack_type = "LL"
      call loose_letters(tmprec.start_zip, tmprec.end_zip, tmprec.cntr)
   end case
end while
close sack_curs
finish report zipreport
end function
############

report zipreport (a_line)
define
  a_line char(80)
output
  top margin 0
  left margin 0
format
  on every row
print a_line
end report

#################################
FUNCTION five_sack(zipcode, cntr)
#################################
define
  rpt_line char(80),
  zipcode char(5),
  cntr smallint

output to report zipreport("")
let rpt_line = "Sack (5 digits) for ", zipcode, "  (",
   cntr using "<<<<<<", " pieces)"
output to report zipreport (rpt_line)
output to report zipreport("")
end function
############

###################################
FUNCTION five_bundle(zipcode, cntr)
###################################
define
  rpt_line char(80),
  zipcode char(5),
  cntr smallint

let rpt_line = "Bundle (5 digits) for ", zipcode, " (",
   cntr using "<<<<<<", " pieces)"
output to report zipreport (rpt_line)
end function
############

##########################################
FUNCTION three_sack(zipstart, zipend, cnt)
##########################################
define
  zipstart char(5),
  zipend char(5),
  cnt smallint,
  rpt_line char(80)

output to report zipreport ("")
let rpt_line = "Sack (3 digits) for ", zipstart, " to ", zipend, "  (",
   cnt using "<<<<<<", " pieces)"
output to report zipreport (rpt_line)
output to report zipreport ("")
end function
############

############################################
FUNCTION three_bundle(zipstart, zipend, cnt)
############################################
define
  zipstart char(5),
  zipend char(5),
  cnt smallint,
  rpt_line char(80)

let rpt_line = "Bundle (3 digits) for ", zipstart, " to ", zipend, "  (",
   cnt using "<<<<<<", " pieces)"
output to report zipreport (rpt_line)
end function
############

###########################################
FUNCTION misc_sack(sackstart, sackend, cnt)
###########################################
define
  sackstart char(5),
  sackend char(5),
  cnt smallint,
  rpt_line char(80)

output to report zipreport ("")
let rpt_line = "Loose Sack for ", sackstart
if length(sackend) = 0 or sackstart = sackend then
  let rpt_line = rpt_line clipped, "  (", cnt using "<<<<<<", " pieces)"
else
  let rpt_line = rpt_line clipped,  " to ",
    sackend, "  (", cnt using "<<<<<<", " pieces)"
end if
output to report zipreport (rpt_line)
output to report zipreport ("")
end function
############

#############################################
FUNCTION loose_letters(zipstart, zipend, cnt)
#############################################
define
  zipstart char(5),
  zipend char(5),
  cnt smallint,
  rpt_line char(80)

let rpt_line = "Loose Letters for ", zipstart
if length(zipend) = 0 or zipstart = zipend then
  let rpt_line = rpt_line clipped, "  (", cnt using "<<<<<<", " pieces)"
else
  let rpt_line = rpt_line clipped,  " to ",
    zipend, "  (", cnt using "<<<<<<", " pieces)"
end if
output to report zipreport (rpt_line)
end function
############

##################################
FUNCTION build_sack(zipcode, cntr)
##################################
define
  zipcode char(5),
  cntr smallint,
  zip3 char(3)

if sacrec.stzip is null or length(sacrec.stzip) = 0
then
  let sacrec.stzip = zipcode
  let sacrec.cntr = cntr
  return "N"
end if

let zip3 = zipcode[1,3]
if zip3 = sacrec.stzip[1,3] then     # belongs in this sack ...
  let sacrec.endzip = zipcode
  let sacrec.cntr = sacrec.cntr + cntr
  return "N"
end if
                                     # sack is finished one way or another
if sacrec.cntr >= 125 then
  let sacrec.sacktype = "3S"
else
  let sacrec.sacktype = "MS"
end if
return "Y"
end function
############

####################################
FUNCTION build_bundle(zipcode, cntr)
####################################
define
  zipcode char(5),
  cntr smallint,
  zip3 char(3)

if bundle_rec.stzip is null or length(bundle_rec.stzip) = 0
then
  let bundle_rec.stzip = zipcode
  let bundle_rec.cntr = cntr
  if sacrec.cntr is not null and sacrec.cntr > 0 then
    if zipcode[1,3] = sacrec.stzip[1,3] then else
      if sacrec.cntr >= 125 then
        insert into zipsacks values
          (0, sacrec.stzip, bundle_rec.endzip, sacrec.cntr, "3S")
      else
        insert into zipsacks values
          (0, sacrec.stzip, bundle_rec.endzip, sacrec.cntr, "MS")
      end if
      initialize sacrec.* to null
    end if
  end if
  return "N"
end if

let zip3 = zipcode[1,3]
if zip3 = bundle_rec.stzip[1,3]
then     # belongs in this bundle ...
  let bundle_rec.endzip = zipcode
  let bundle_rec.cntr = bundle_rec.cntr + cntr
  return "N"
end if
                                     # bundle is finished one way or another
if bundle_rec.cntr >= 10 then
  let bundle_rec.bundletype = "3B"
else
  let bundle_rec.bundletype = "LL"
end if
return "Y"
end function
############

####################
FUNCTION test_sack()
####################
define
  flg char(2)

call test_bundle()
if sacrec.cntr is not null and sacrec.cntr > 0 then
  if sacrec.cntr >= 125 then
    let flg = "3S"
  else
    let flg = "MS"
  end if
  insert into zipsacks values (0,sacrec.stzip, sacrec.endzip, sacrec.cntr, flg)
  initialize sacrec.* to null
end if
end function
############

######################
FUNCTION test_bundle()
######################
define
  flg char(2)

if bundle_rec.cntr is not null and bundle_rec.cntr > 0 then
  if bundle_rec.cntr >= 10 then
    let flg = "3B"
    insert into zipsacks values
      (0,bundle_rec.stzip, bundle_rec.endzip, bundle_rec.cntr, flg)
    call build_sack(bundle_rec.stzip, bundle_rec.cntr) returning sack_flag
    if sack_flag = "Y" then    # sack needs to be created (record doesn't fit)
      insert into zipsacks values
        (0,sacrec.stzip, sacrec.endzip, sacrec.cntr, sacrec.sacktype)
      initialize sacrec.* to null
      call build_sack(bundle_rec.stzip, bundle_rec.cntr) returning sack_flag
    end if
  else
    let flg = "LL"
    insert into zipsacks values
      (0,bundle_rec.stzip, bundle_rec.endzip, bundle_rec.cntr, flg)
  end if
  initialize bundle_rec.* to null
end if
end function
############

  This is the end of the code.  A couple of improvements would be the
inclusion of sorting by state and controlling the size of sacks.  I'm
not going to include the guidelines provided by the Post Office but
basically they want homogenous sacks of 125 pieces or 15 lbs, and
they want homogenous bundles of ten or more letters.  It would be
nice to control the counters to build sacks of specifics sizes.  This
would change, of course, with variations in letter sizes.

  Well, if anyone wants to play with this here it is.  I'd like to
hear of suggestions for improvements etc, but for now I'm on to other
tasks.

---------- There are more things in heaven and earth, Horatio, ----------------
Scott Holmes            <[email-masked]>          Informix 4GL Applications
--------------- Than are dreamt of in your philosophy. ------------------------