Selecting Random Numbers and Records on the Force.com Platform Part 1

Summertime air travel is in full swing here in the US and the Transportation Security Agency is out performing extra, “random” screening at various gates. I’ve watched a few of these recently and I’ve been left with the impression that random is, apparently, decided by the screeners and doesn’t appear random at all. Being a problem solver, I started thinking of how I could use the Force.com platform to help the TSA choose random travelers in a more efficient fashion.

Random Numbers

First, we need some randomness. There are a couple of ways to generate random numbers on the Force.com platform. The first is to call either of the static methods getRandomInteger() or getRandomLong() in the Crypto class. The second is to call either the random() or rint() methods of the Math class. Armed with this knowledge, let’s create a REST based web service that agents can use, at the jet bridge, from their mobile device that instructs them which passengers to choose for extra screening.

There’s a number of ways to do this. The first selects passengers by name, from a list, for random screening. The second, and I believe better, is to provide a list of numbers that show the line positions of people to pull without knowing their identity. For example {1,10,19,103} would tell me to pull the first, tenth, nineteenth and hundred and third passenger out for extra screening.  We could combine the two as well.

Getting Started

Figure 1: Passenger Data Model

Figure 1 shows the simple data model that I am using for this example. I’ve also created a package with the objects and code you can install from this link 

The Code

With the data model in place, let’s start writing some code. The first thing we’ll need is a method that generates a random number. We could easily write it like this:

public Integer randomWithLimit(Integer upperLimit){
        Integer rand = Math.round(Math.random()*1000);
        return Math.mod(rand, upperLimit);
    }

A couple things are happening here. First, on line two we are calling Math.random() which will return a number greater than 0.0 and less than 1. I multiply by 1000 because I want a large range  (to cover hundreds of passengers) and when I round, I get an integer with a range from 0 to 999. Since my flights will have different passenger totals and since it doesn’t make sense to select more passengers for screening than are on the flight, I put a limit on the return value.  By moding the random number with the upper limit, I make sure I never return an integer bigger than what I want. For instance, if I have a flight with 139 people on it, I would pass that as the limit so I never got a result that said pull the two hundred and tenth passenger out of line. Since we don’t want to randomly “select” every passenger let’s put another limit on our method. Let’s specify how many passengers we want to select. We’ll change our code a bit as follows

public List randomWithLimit(Integer upperLimit, Integer numberOfRands)
{
   Listselected = new List();
   for (Integer i =0; i< numberOfRands; i++){
      Integer rand = Math.mod(Math.round(Math.random()*1000));
      selected.add(rand);
   }
 return selected
}

 
Here’s the test:

@isTest
public class RandomScreeningTest{	
    private static RandomScreen randGen = new RandomScreen();   
    public static testMethod void testRandomList(){	   
        List<Integer> testResults = randGen.randomWithLimit(139, 15);
        System.assertEquals(testResults.size(), 15);
        for(Integer i: testResults){
            System.assert(i < 139);
            System.debug(i);
        }
    }    
    public static testMethod void testSingleRandom(){
        Integer testResult = randGen.randomWithLimit(234);
        System.assert(testResult < 234);
        System.debug(testResult);
    }
}

Now we have a method that returns n number of random integers between 0 and upperLimit, which we want. The last thing left to do is make this code mobile friendly. We’ll do that by writing the following Apex REST service.

APEX Rest

@RestResource(urlMapping='/screeninglist/*')
global class RandomScreeningList{
	@HttpGet
        global static List getRandomList(){
            List<Integer> selected = new List();
            //Get query string parameters from request and convert necessary data types
            RestRequest request = RestContext.request;
            Map<String, String>params = request.params;
            Decimal flightNumber = Decimal.valueOf(params.get('flightNumber'));
            Date flightDate = Date.valueOf(params.get('date'));
            Integer numberOfRands = Integer.valueOf(params.get('rands'));
            Flight__c flight = [Select Flight_Number__c, Flight_Date__c, Total_Passengers__c FROM Flight__c where Flight_Date__c = :flightDate and Flight_Number__c= :flightNumber];
            if(flight != null){
            	//Set the upper limit to Total_Passengers__c
                Integer upperLimit = Integer.valueOf(flight.Total_Passengers__c);
                selected = new RandomScreen().randomWithLimit(upperLimit,numberOfRands);
            }
        	return selected;
        }
}

 

Of course we’ll need to test our Apex Rest service before we can deploy it outside of a sandbox. Here’s the test class

@isTest
public class RandomScreenListTest{
	public static testMethod void testRandomRest(){
        // create and insert a test flight
        Flight__c flight = new Flight__c();
        flight.Flight_Date__c = Date.today();
        flight.Flight_Number__c = 99;
        flight.Total_Passengers__c = 149;
       	insert flight;

        RestRequest req = new RestRequest();
        // change this for your instance
        req.requestURI = 'https:/na15.salesforce.com/services/apexrest/screeninglist/';
    	// build the request
        req.httpMethod = 'GET';
        req.addParameter('flightNumber', '99');
        req.addParameter('date', '7/10/2013');
        req.addParameter('rands', '5');
        RestContext.request = req;
        Test.startTest();
        	List<Integer> testList = RandomScreeningList.getRandomList();
        Test.stopTest();
        System.assert(testList.size()==5);
        for(Integer i =0; i < 5; i++){
            //assert no rand is greater than limit determined by Flight__c.Total_Passengers__c
            System.assert(testList[i] < 149);
        }
    }
}

All that’s left to do is build, or add this functionality, to a mobile app. Here I show testing the REST Service with Workbench. You could also use another client like curl or Postman but you would have to handle the OAuth authentication and URL escaping yourself.

Here I’ve logged into Workbench. Selected Utilities->Rest Explorer from the menu and inserted the following test URL: /services/apexrest/screeninglist/?flightNumber=99&date=7/10/2013&rands=5  (your data may be different)

In part 1 I’ve shown how to generate random numbers and use them as part of a selection process. In part 2, I’ll modify this slightly to show pulling random records from salesforce. This isn’t a perfect solution and there are some additional details that would need to be added to a production implementation. One thing is that I don’t check for duplicate randoms within my list. It’s possible my return value could look like this: {1,3,100,19,19}. I’m sure readers will find others. I’ll leave those modifications as an exercise to the reader. As always, if you think of a way to make this better please comment.

Published
July 15, 2013
Topics:

Leave your comments...

Selecting Random Numbers and Records on the Force.com Platform Part 1