Look Up Data in a Qualified Data Structure

Article ID: 19710

Q: Can I use the %LOOKUP BIF to search an array of qualified data structures?

A: When the array is a simple array that's inside a data structure, you can search it with %LOOKUP in the same manner that you would a standalone field. It doesn't matter if it's qualified or not, as long as it's a simple array.

In many cases, people prefer to load their data into many arrays where each array is a subfield of a data structure. You can use %LOOKUP to search them, and you can use SORTA to sort them by different fields. There are examples of this in the ILE RPG Reference manual, so I won't include one here.

However, when the array is an array of data structures, it can't be used with the %LOOKUP BIF. (Perhaps that feature will be available in a future release of RPG!)

All %LOOKUP does is loop through an array and compare each array element to the value that you're searching for. It's easy enough to do the same thing by writing a simple loop, such as the one shown in the following code snippet:

    D Wholesaler    ds             Dim(300) Qualified
    D    Name                   20a
    D    Amount                 12s 2

    D found         s           10I 0
    D x             s           10I 0

     /free

        Found = 0;
        for x = 1 to %elem(Wholesaler);
           if (Wholesaler(x).name = SearchArg);
              Found = x;
              leave;
           endif;
        endfor;

Once this code has run, the FOUND variable will contain the number of the array element where the wholesaler's name was the same as the value in the SearchArg field.

One of the newer features of the %LOOKUP BIF is that it can use a binary search algorithm when operating on a simple array. This is much faster than looping through each array element. This happens automatically when you specify the ASCEND or DESCEND keyword on the D- spec where the array is defined. The only "gotcha," however, is that you must sort the array before using the %LOOKUP BIF, or it'll return incorrect results.

You can do the same thing on an array of qualified data structures using two APIs. The qsort() API is used to sort the array, and the bsearch() API to perform the binary search.

In order to search data or to sort it, you have to be able to compare one element to another. That's easy for %LOOKUP to do because the compiler knows what sort of data you have in your program and can insert the code to do the comparison. It's not as easy with an API that's already been compiled and doesn't know anything about your program's definitions!

The solution was to ask the programmer to provide a routine that does the comparison. The programmer can then write a subprocedure. The subprocedure provides the code to compare the two array elements and returns a -1 when the first element should come first in the results, a 1 when the second one should come first, and a 0 when they're equal. You can then pass the API a pointer to this subprocedure so that it can call your routine whenever it needs to make a comparison.

The following code demonstrates creating a simple telephone directory as an array of qualified data structures. It uses the qsort() API to sort the array by last name and the bsearch() API to look those names up using the binary search algorithm:

    H DFTACTGRP(*NO) BNDDIR('QC2LE')

    D qsort           PR                  extproc('qsort')
    D   base                          *   value
    D   num                         10U 0 value
    D   width                       10U 0 value
    D   compare                       *   procptr value

    D bsearch         PR              *   extproc('bsearch')
    D   key                           *   value
    D   base                          *   value
    D   num                         10U 0 value
    D   size                        10U 0 value
    D   compare                       *   procptr value

    D myTemplate      ds                  qualified
    D                                     based(Template)
    D   LastName                    20A
    D   FirstName                   20A
    D   ext                         10I 0

    D users           ds                  likeds(myTemplate)
    D                                     dim(100)

    D p_match         s               *
    D match           ds                  likeds(myTemplate)
    D                                     based(p_match)

    D key             ds                  likeds(myTemplate)

    D CompByLast      pr            10I 0
    D   elem1                             likeds(myTemplate)
    D   elem2                             likeds(myTemplate)

    D x               s             10I 0
    D numUsers        s             10I 0
    D msg             s             52A

     /free

        // -------------------------------------------
        // Create some sample data
        // -------------------------------------------

         x = 1;
         users(x).LastName  = 'Klement';
         users(x).FirstName = 'Scott';
         users(x).ext       = 292;

         x = x + 1;
         users(x).LastName  = 'Lewis';
         users(x).FirstName = 'Doug';
         users(x).ext       = 280;

         x = x + 1;
         users(x).LastName  = 'Bizub';
         users(x).FirstName = 'James';
         users(x).ext       = 291;

         x = x + 1;
         users(x).LastName  = 'Michuda';
         users(x).FirstName = 'Michael';
         users(x).ext       = 209;

         x = x + 1;
         users(x).LastName  = 'Solano';
         users(x).FirstName = 'Maria';
         users(x).ext       = 216;

         x = x + 1;
         users(x).LastName  = 'Straw';
         users(x).FirstName = 'Penny';
         users(x).ext       = 302;

         x = x + 1;
         users(x).LastName  = 'Wiesner';
         users(x).FirstName = 'Beatrice';
         users(x).ext       = 200;

         x = x + 1;
         users(x).LastName  = 'Vogl';
         users(x).FirstName = 'Jackie';
         users(x).ext       = 201;

         x = x + 1;
         users(x).LastName  = 'Sotski';
         users(x).FirstName = 'Daniel';
         users(x).ext       = 203;

         numUsers = x;
        // -------------------------------------------
        // Sort array by Last name
        // -------------------------------------------

        qsort( %addr(users)
             : numUsers
             : %size(myTemplate)
             : %paddr(CompByLast) );

        // -------------------------------------------
        // Search for 'Klement'
        //   then for 'Michuda'
        // -------------------------------------------

        key.LastName = 'Klement';

        p_match = bsearch( %addr(key)
                         : %addr(users)
                         : numUsers
                         : %size(myTemplate)
                         : %paddr(CompByLast) );

        if (p_match = *NULL);
            msg = %trimr(key.lastname) + ' not found!';
            dsply msg;
        else;
            msg = %trimr(match.lastname) + ' is ext '
                + %char(match.ext);
            dsply msg;
        endif;

        key.LastName = 'Michuda';

        p_match = bsearch( %addr(key)
                         : %addr(users)
                         : numUsers
                         : %size(myTemplate)
                         : %paddr(CompByLast) );

        if (p_match = *NULL);
            msg = %trimr(key.lastname) + ' not found!';
            dsply msg;
        else;
            msg = %trimr(match.lastname) + ' is ext '
                  + %char(match.ext);
            dsply msg;
        endif;

        *inlr = *on;
     /end-free
     *++++++++++++++++++++++++++++++++++++++++++++++++++++
     * Compare Two Elements, using Last Name as the
     *       only key.
     *++++++++++++++++++++++++++++++++++++++++++++++++++++
    P CompByLast      B
    D CompByLast      PI            10I 0
    D   elem1                             likeds(myTemplate)
    D   elem2                             likeds(myTemplate)
     /free

         select;
         when (elem1.LastName < elem2.LastName);
            return -1;
         when (elem1.LastName > elem2.LastName);
            return 1;
         other;
            return 0;
         endsl;

     /end-free
    P                 E

Since you control the way things are compared, you have a bit more flexibility than you would with either SORTA or %LOOKUP.

For example, just by changing out the compare routine, you can have it search by both first and last name and, a few lines later in the same program, you can switch to searching by extension number. The following code demonstrates this. (I've omitted the D-specs and code to populate the sample array, since it's the same as the code in the previous section.)

        // -------------------------------------------
        // How about searching by complete name
        //   (first & last)
        // -------------------------------------------

        qsort( %addr(users)
             : numUsers
             : %size(myTemplate)
             : %paddr(CompByFirst) );

        key.FirstName = 'Scott';
        key.LastName = 'Klement';

        p_match = bsearch( %addr(key)
                         : %addr(users)
                         : numUsers
                         : %size(myTemplate)
                         : %paddr(CompByFirst) );

        if (p_match = *NULL);
            msg = %trimr(key.firstname) + ' '
                + %trimr(key.lastname) + ' not found!';
            dsply msg;
        else;
            msg = %trimr(match.firstname) + ' '
                + %trimr(match.lastname) + ' is ext '
                + %char(match.ext);
            dsply msg;
        endif;

        // -------------------------------------------
        // How about searching by extension number
        // -------------------------------------------

        qsort( %addr(users)
             : numUsers
             : %size(myTemplate)
             : %paddr(CompByExt) );

        key.ext = 291;

        p_match = bsearch( %addr(key)
                         : %addr(users)
                         : numUsers
                         : %size(myTemplate)
                         : %paddr(CompByExt) );

        if (p_match = *NULL);
            msg = %char(key.ext) + ' not found!';
            dsply msg;
        else;
            msg = %trimr(match.firstname) + ' '
                + %trimr(match.lastname) + ' is ext '
                + %char(match.ext);
            dsply msg;
        endif;
        *INLR = *ON;
     /end-free
     *++++++++++++++++++++++++++++++++++++++++++++++++++++
     * Compare Two Elements, using a composite key
     *     created from the first & last name
     *++++++++++++++++++++++++++++++++++++++++++++++++++++
    P CompByFirst     B
    D CompByFirst     PI            10I 0
    D   elem1                             likeds(myTemplate)
    D   elem2                             likeds(myTemplate)
     /free

         select;
         when (elem1.FirstName < elem2.FirstName);
            return -1;
         when (elem1.FirstName > elem2.FirstName);
            return 1;
         when (elem1.LastName < elem2.LastName);
            return -1;
         when (elem1.LastName > elem2.LastName);
            return 1;
         other;
            return 0;
         endsl;

     /end-free
    P                 E
     *++++++++++++++++++++++++++++++++++++++++++++++++++++
     * Compare Two Elements, using the telephone ext
     *     as the key
     *++++++++++++++++++++++++++++++++++++++++++++++++++++
    P CompByExt       B
    D CompByExt       PI            10I 0
    D   elem1                             likeds(myTemplate)
    D   elem2                             likeds(myTemplate)
     /free

         select;
         when (elem1.Ext < elem2.ext);
            return -1;
         when (elem1.Ext > elem2.ext);
            return 1;
         other;
            return 0;
         endsl;

     /end-free
    P                 E

But wait... there's more! Again, since you can control the way things are compared, you could make it case-insensitive if you wanted to, which %LOOKUP can't do. In the following example, I've done a case- insensitive search by the last name:

        // -------------------------------------------
        //  You can also do a case-insensitive sort
        //   and search just by changing the
        //   way the elements are compared
        // -------------------------------------------

        qsort( %addr(users)
             : numUsers
             : %size(myTemplate)
             : %paddr(CompCase) );

        key.LastName = 'stRaW';

        p_match = bsearch( %addr(key)
                         : %addr(users)
                         : numUsers
                         : %size(myTemplate)
                         : %paddr(CompCase) );

        if (p_match = *NULL);
            msg = %trimr(key.lastname) + ' not found!';
            dsply msg;
        else;
            msg = %trimr(match.firstname) + ' '
                + %trimr(match.lastname) + ' is ext '
                + %char(match.ext);
            dsply msg;
        endif;

        *inlr = *on;

     /end-free

     *++++++++++++++++++++++++++++++++++++++++++++++++++++
     * This is the same as CompByLast, except that it's
     *  not case-sensitive.
     *++++++++++++++++++++++++++++++++++++++++++++++++++++
    P CompCase        B
    D CompCase        PI            10I 0
    D   elem1                             likeds(myTemplate)
    D   elem2                             likeds(myTemplate)

    D upper           c                   
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    D lower           c                   
'abcdefghijklmnopqrstuvwxyz'

    D last1           s                   like(myTemplate.lastname)
    D last2           s                   like(myTemplate.lastname)
     /free

         last1 = %xlate(lower:upper: elem1.lastname);
         last2 = %xlate(lower:upper: elem2.lastname);

         select;
         when (last1 < last2);
            return -1;
         when (last1 > last2);
            return 1;
         other;
            return 0;
         endsl;

     /end-free
    P                 E

ProVIP Sponsors

ProVIP Sponsors