• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • I Was Just Wondering. . .

    December 14, 2011 Hey, Ted

    Note: The code accompanying this article is available for download here.

    I was wondering if you have come across any techniques using subprocedures to simulate associative arrays in RPG IV. I’ve just started experimenting with the idea. I’d like to create dynamic associative arrays using service programs and user indexes.

    –Steve

    As a matter of fact, I had thought about implementing associative arrays in RPG, Steve. I’ll share my thoughts on that topic. But first, let me talk about wondering.

    I do a lot of wondering. You’ve seen the results of many of my wonderings in previous issues of this august newsletter, especially the end-of-year issues, when I have written about things like solving sodoku puzzles and finding a path through a maze. Wondering is one of the best skills a programmer can have. I’d go so far to say that wondering is one of the best skills a person of any profession can have, except maybe for tax preparers, bureaucrats, and politicians.

    Earlier this year, I wondered about the digital alarm clock that sits by my bed. The numbers are made up of illuminated line segments, like this:

     _       _   _         _
    | |  |   _|  _|  |_|  |_
    |_|  |  |_   _|    |   _|
    

    I wondered if I could write a program that would convert a string of digits into a digital-clock facsimile. Well, I could. I wrote an RPG subprocedure that accepted a string of digits as input and produces three strings of blanks, underscores and vertical bars as output.

    After I had that working, I wondered why limit the domain to numerals, so I added some letters, such as these:

     _    _   _            
    |    |_  |_   |_|    |  
    |_   |_  |    | |  |_|  
    

    And I could do that, too.

    Then I wondered if I might be able to create more letters if I added slashes and back slashes.

         _   
    |/  |_|  /
    |  |   /
    

    And I could do that, too.

    Recently I was reading a Web forum just for a change of pace. The messages were written in Esperanto, a language that I can read with the aid of a dictionary but don’t claim to be able to speak. Someone said that he’d been speaking Esperanto for 35 years. However, he failed to end some direct objects with the letter n. I got to wondering, not whether he’d really been using Esperanto for 35 years, because I don’t care about that, but whether I could write a program that would help a fellow catch grammatical errors. After all, Microsoft Word does it.

    I decided that writing a grammar checker would take more time and effort, in research and development, than I could devote to it. However, maybe I could write a sort of “visual explain” that would tell the function and part of speech of each word.

    And I did. I wrote it in Visual Basic 2010 Express. Here’s an example of the output.

    mia	adjective
    fratino	noun
    havas	verb, present tense
    multajn	adjective, plural, accusative
    bonajn	adjective, plural, accusative
    amikojn	noun, plural, accusative
    

    I see that every word is evaluated to be what I intended it to be. For example, if any of the last three words did not have “accusative” in the description, I would know that I had omitted that final n. I could add more tests to what I’ve got, and maybe I will someday.

    I may never use this program, but if I ever decide to post to an Esperanto forum, I will run my text through my program first. And I doubt seriously I’ll ever use the digital-clock code in production. In fact, I don’t even know where it is at the moment. But I don’t think the time I spent on these little projects was wasted, because I tackled a couple of problems I’d never tackled before.

    Now let me return to Steve’s question.

    An associative array is a collection of keys and values. We’re accustomed to indexing array elements by position in the list. With an associative array, elements are referenced by names. A good introductory article is The Power of Associative Arrays, by Howard Fosdick.

    For example, here’s a REXX procedure in which the values 26 and 28 are assigned to elements Chuck and Mary, respectively, of the age array, and then those values are sent to the standard output device.

    age. = 0
    age.Chuck = 26
    age.Mary  = 28
    say age.Chuck
    say age.Mary
    

    The best idea I could come up with was to use two arrays–one to hold the key value and one to hold the data value–and then use the usual array lookup op codes to access the arrays. Something like this:

    H dftactgrp(*no) actgrp(*new)
    H option(*srcstmt: *nodebugio)
    
    D                 ds
    D ArrayData                           dim(24)
    D   Name                        16a   overlay(ArrayData: 1)     inz
    D   Age                          3p 0 overlay(ArrayData: *next) inz
    D NbrActive       s             10i 0
    
    D AnAge           s              3p 0
    D SomeGuy         s             20a
    
    D SetAge          pr
    D  inName                       20a   const
    D  inAge                         3p 0 const
    D GetAge          pr             3p 0
    D  inName                       16a   const
    
     /free
         *inlr = *on;
         SetAge ('Bill E. Klub': 29);
         SetAge ('Nan Tuckit': 18);
    
         AnAge = GetAge ('Nan Tuckit');
         AnAge = GetAge ('Jerry Mander');
         SomeGuy = 'Bill E. Klub';
         AnAge = GetAge (SomeGuy);
    
         return;
     /end-free
     * ============================================
    P SetAge          b
    D                 pi
    D  inName                       20a   const
    D  inAge                         3p 0 const
     *** locals
    D Ndx             s             10i 0
     /free
         Ndx = %lookup(inName: Name: 1: NbrActive);
         if Ndx = *zero;
            NbrActive += 1;
            Ndx = NbrActive;
            Name (Ndx) = inName;
         endif;
         Age (Ndx) += inAge;
     /end-free
    P                 e
     * ============================================
    P GetAge          b
    D                 pi             3p 0
    D  inName                       16a   const
     *** locals
    D Ndx             s             10i 0
     /free
         Ndx = %lookup(inName: Name: 1: NbrActive);
         if Ndx = *zero;
            return *zero;
         endif;
         return Age (Ndx);
     /end-free
    P                 e
    

    The grunt work of storing and retrieving has been relegated to subprocedures. The syntax to store and retrieve ages by name is not as easy as the REXX associative array syntax, but it’s simple enough.

    SetAge ('Nan Tuckit': 18);
    AnAge = GetAge ('Nan Tuckit');
    

    I continued to wonder and came up with something unexpected. I came up with an easy way to add a recap to a report.

    I created two related arrays: one to hold a key value, and one to hold an amount of currency. I wrote subprocedures to add to an amount element: one to tell me how many array elements are in use, and one to return an amount by position. (I wrote a subprocedure to get by name, too, but I didn’t need it for this example.) Here’s the RPG source code for my module.

    H nomain
    H option(*srcstmt: *nodebugio)
    
    D                 ds
    D ArrayData                           dim(24) ascend
    D   Codes                       16a   overlay(ArrayData: 1)     inz
    D   TotalDue                     7p 2 overlay(ArrayData: *next) inz
    D NbrOfActiveCodes...
    D                 s             10i 0
    
    D/copy prototypes,associativ
    
     * ============================================
    P AddQty          b                   export
    D                 pi
    D  inCode                       16a   const
    D  inQty                         7p 2 const
     *** locals
    D Ndx             s             10i 0
     /free
         Ndx = %lookup(inCode: Codes: 1: NbrOfActiveCodes);
         if Ndx = *zero;
            NbrOfActiveCodes += 1;
            Ndx = NbrOfActiveCodes;
            Codes (Ndx) = inCode;
            sorta %subarr(Codes: 1: NbrOfActiveCodes);
            Ndx = %lookup(inCode: Codes: 1: NbrOfActiveCodes);
         endif;
         TotalDue (Ndx) += inQty;
     /end-free
    P                 e
     * ============================================
    P GetQty          b                   export
    D                 pi             7p 2
    D  inCode                       16a   const
     *** locals
    D Ndx             s             10i 0
    D Qty             s              7p 2
     /free
         Ndx = %lookup(inCode: Codes: 1: NbrOfActiveCodes);
         if Ndx = *zero;
            return *zero;
         endif;
         return TotalDue (Ndx);
     /end-free
    P                 e
     * ============================================
    P GetQtyByPosition...
    P                 b                   export
    D                 pi
    D  inNdx                        10u 0 const
    D  ouCode                       16a
    D  ouQty                         7p 2
     *** locals
     /free
         if (inNdx = *zero)
         or (inNdx > NbrOfActiveCodes);
            clear ouCode;
            clear ouQty;
         endif;
         ouCode = Codes (inNdx);
         ouQty  = TotalDue (inNdx);
     /end-free
    P                 e
     * ============================================
    P GetNbrActive    b                   export
    D                 pi            10u 0
     /free
         return NbrOfActiveCodes;
     /end-free
    P                 e
    

    Notice that the AddQty subprocedure accumulates based on key value. If it gets a key it’s never seen before, it sets aside another array element and sorts the array.

    I created the module and created a service program from it. Then I wrote a calling program.

    H option(*srcstmt: *nodebugio)
    
    Fqcustcdt  if   e             disk    usropn prefix(in_)
    Fqad3063p  o    e             printer usropn prefix(ou_)
    
    D RecapSize       s             10u 0
    D Ndx             s             10u 0
    
    D/copy prototypes,associativ
    
     /free
         *inlr = *on;
         open qcustcdt;
         open qad3063p;
         write header;
         write space;
    
         dow '1';
            read cusrec;
            if %eof();
               leave;
            endif;
            ou_Name = in_LstNam + ' ' + in_Init;
           ou_City = in_City;
           ou_State = in_State;
           ou_BalDue = in_BalDue;
           ou_ChgCod = in_ChgCod;
           write Detail;
           AddQty ('Chg code: ' + %editc(in_ChgCod:'X'): in_BalDue);
           AddQty ('State: ' + in_State: in_BalDue);
        enddo;
    
        write space;
    
        RecapSize = GetNbrActive();
    
        for Ndx = 1 to RecapSize;
           GetQtyByPosition (Ndx: ou_SumDesc: ou_Balance);
           write Summary;
        endfor;
        close *all;
        return;
    /end-free
    

    Building two recaps is a simple matter of calling the AddQty routine twice. Printing the recaps is a trivial matter of calling GetQtyByPosition within a loop. The report looks like this:

    VERY IMPORTANT REPORT
               
    Henning  G K   Dallas   TX   3      37.00
    Jones    B D   Clay     NY   1     100.00
    Vine     S S   Broton   VT   1     439.00
    Johnson  J A   Helen    GA   2   3,987.50
    Tyron    W E   Hector   NY   1
    Stevens  K L   Denver   CO   1      58.75
    Alison   J S   Isle     MN   3      10.00
    Doe      J W   Sutter   CA   2     250.00
    Thomas   A N   Casper   WY   2
    Williams E D   Dallas   TX   1      25.00
    Lee      F L   Hector   NY   2     489.50
    Abraham  M T   Isle     MN   3     500.00
    
    Chg code: 1             622.75
    Chg code: 2           4,727.00
    Chg code: 3             547.00
    State: CA               250.00
    State: CO                58.75
    State: GA             3,987.50
    State: MN               510.00
    State: NY               589.50
    State: TX                62.00
    State: VT               439.00
    State: WY                  .00
    

    Not bad for a guy who was just goofing off. I’ve attached the code, in case you want to play with it.

    To make a generalized service program to handle associative arrays, as Steve suggested, seemed out of the question. However, I know someone else who has taken this idea of associative arrays even further, and if I can manage to find the time to edit his submission, I will publish his article and blow off your toupee.

    Wondering is a good thing, and people ought to do more of it. May we all find a heightened sense of wonder in 2012.

    –Ted

    RELATED STORIES

    Two A-maze-ing Programs

    Stuff I Didn’t Publish This Year



                         Post this story to del.icio.us
                   Post this story to Digg
        Post this story to Slashdot

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    WorksRight Software

    Do you need area code information?
    Do you need ZIP Code information?
    Do you need ZIP+4 information?
    Do you need city name information?
    Do you need county information?
    Do you need a nearest dealer locator system?

    We can HELP! We have affordable AS/400 software and data to do all of the above. Whether you need a simple city name retrieval system or a sophisticated CASS postal coding system, we have it for you!

    The ZIP/CITY system is based on 5-digit ZIP Codes. You can retrieve city names, state names, county names, area codes, time zones, latitude, longitude, and more just by knowing the ZIP Code. We supply information on all the latest area code changes. A nearest dealer locator function is also included. ZIP/CITY includes software, data, monthly updates, and unlimited support. The cost is $495 per year.

    PER/ZIP4 is a sophisticated CASS certified postal coding system for assigning ZIP Codes, ZIP+4, carrier route, and delivery point codes. PER/ZIP4 also provides county names and FIPS codes. PER/ZIP4 can be used interactively, in batch, and with callable programs. PER/ZIP4 includes software, data, monthly updates, and unlimited support. The cost is $3,900 for the first year, and $1,950 for renewal.

    Just call us and we’ll arrange for 30 days FREE use of either ZIP/CITY or PER/ZIP4.

    WorksRight Software, Inc.
    Phone: 601-856-8337
    Fax: 601-856-9432
    Email: software@worksright.com
    Website: www.worksright.com

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Sponsored Links

    looksoftware:  Take your IBM i apps from average to amazing! FREE OnDemand webinar!
    The 400 School:  Fall Training Sale – Discounts up to 40%! RPG IV COBOL CL Admin Security
    CCSS:  Achieving Lights Out Automation in an IBM i environment. Get the Best Practice guide

    IT Jungle Store Top Book Picks

    BACK IN STOCK: Easy Steps to Internet Programming for System i: List Price, $49.95

    The iSeries Express Web Implementer's Guide: List Price, $49.95
    The iSeries Pocket Database Guide: List Price, $59
    The iSeries Pocket SQL Guide: List Price, $59
    The iSeries Pocket WebFacing Primer: List Price, $39
    Migrating to WebSphere Express for iSeries: List Price, $49
    Getting Started with WebSphere Express for iSeries: List Price, $49
    The All-Everything Operating System: List Price, $35
    The Best Joomla! Tutorial Ever!: List Price, $19.95

    Santa Faces Daunting Supply Chain Challenge The World Is Not Going To End In 2012

    Leave a Reply Cancel reply

Volume 11, Number 38 -- December 14, 2011
THIS ISSUE SPONSORED BY:

WorksRight Software
Bytware
Twin Data Corporation

Table of Contents

  • I Was Just Wondering. . .
  • End-of-Year Odds and Ends
  • Admin Alert: Hidden Parameters of the Submit Job Command

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • Public Preview For Watson Code Assistant for i Available Soon
  • COMMON Youth Movement Continues at POWERUp 2025
  • IBM Preserves Memory Investments Across Power10 And Power11
  • Eradani Uses AI For New EDI And API Service
  • Picking Apart IBM’s $150 Billion In US Manufacturing And R&D
  • FAX/400 And CICS For i Are Dead. What Will IBM Kill Next?
  • Fresche Overhauls X-Analysis With Web UI, AI Smarts
  • Is It Time To Add The Rust Programming Language To IBM i?
  • Is IBM Going To Raise Prices On Power10 Expert Care?
  • IBM i PTF Guide, Volume 27, Number 20

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle