[SalesForce] getting 3 random elements from a List

in our customer service system, when a customer phones up, the inquiries officer takes the customer through questions built out of fields populated on the customer record to check identity.

Now I have to randomly select 3 questions from the populated fields to present to the user.

Currently I have a List of class <Question> defined as below in my controller:

//Declare question variables 
public list<Question> GroupOneQuestions {get; set;}
public list<Question> GroupTwoQuestions {get; set;}
public list<Question> ActualQuestions   {get; set;}

public class Question{
    public string UniqueId  {get; set;}
    public string Quest     {get; set;}
    public string Answer    {get; set;}
    public string Link      {get; set;}
    public string Outcome   {get; set;}
}

then this code runs on page load to build the list of questions:

private void BuildGroup1Questions(){

    system.debug('BuildGroup1Questions');
    //declare G1question variable
    GroupOneQuestions = new list<Question>();
    integer i = 0;

    system.debug('******* 1 = BuildGroup1Questions');

    if(person.Salutation__c == 'Mrs' && !HelperMethods_v1.ValueIsNull(person.Maiden_Name__c)){
        system.debug('show MaindenName question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Maiden Name';
        tmp.Answer      = person.Maiden_Name__c;
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }

    system.debug('******* 2 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.Date_of_Birth__c)){
        system.debug('show DOB question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Date of Birth';
        tmp.Answer      = HelperMethods_v1.DStr(person.Date_of_Birth__c, 'dd MMMM yyyy');
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }

    system.debug('******* 3 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.Town_of_Birth__c)){
        system.debug('show Town_of_Birth__c question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Town of Birth';
        tmp.Answer      = person.Town_of_Birth__c;
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }

    system.debug('******* 4 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.Answer_to_question__c)){
        system.debug('show Mother\'s Maiden Name question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Mother\'s Maiden Name';
        tmp.Answer      = person.Answer_to_question__c;
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }

    system.debug('******* 5 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.ID_Passport__c)){
        system.debug('show Passport/Identity Number question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Passport/Identity Number';
        tmp.Answer      = person.ID_Passport__c+' ('+person.Passport_Country_of_Issue__c+')';
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }

    system.debug('******* 6 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.National_Insurance_No__c)){
        system.debug('show National_Insurance_No__c question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'National Insurance/Tax ID';
        tmp.Answer      = person.National_Insurance_No__c;
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }       

    system.debug('******* 7 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.Driving_License_No__c)){
        system.debug('show Driving_License_No__c question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Driving License Number';
        tmp.Answer      = person.Driving_License_No__c+' ('+person.Country_of_Driving_License__c+')';
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }       

    system.debug('******* 8 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.Memorable_Date__c)){
        system.debug('show Memorable_Date__c question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Memorable Date';
        tmp.Answer      = HelperMethods_v1.DStr(person.Memorable_Date__c, 'dd MMMM yyyy');
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }
    else if(!helpermethods_v1.valueisnull(mEQDPA_Answers)){

        if(!HelperMethods_v1.ValueIsNull(mEQDPA_Answers.get('MemorableDate_ac'))){
            system.debug('show MemorableDate_ac question = true');
            Question tmp    = new Question();
            tmp.Quest       = 'Memorable Date';
            tmp.Answer      = mEQDPA_Answers.get('MemorableDate_ac');
            tmp.UniqueId    = string.valueof(i);
            i++;
            GroupOneQuestions.add(tmp);
        }
    }           

    system.debug('******* 9 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.Memorable_Street_Name__c)){
        system.debug('show Memorable_Street_Name__c question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Memorable Street Name';
        tmp.Answer      = person.Memorable_Street_Name__c;
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }                   

    system.debug('******* 10 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.Memorable_first_Car__c)){
        system.debug('show Memorable_first_Car__c question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Memorable First Car';
        tmp.Answer      = person.Memorable_first_Car__c;
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }   

    system.debug('******* 11 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(person.Memorable_Colour__c)){
        system.debug('show Memorable_Colour__c question = true');
        Question tmp    = new Question();
        tmp.Quest       = 'Memorable Colour';
        tmp.Answer      = person.Memorable_Colour__c;
        tmp.UniqueId    = string.valueof(i);
        i++;
        GroupOneQuestions.add(tmp);
    }           

    system.debug('******* 12 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(CFEValue)){
        if(CFEValue.size() > 0){
            Question tmp    = new Question();
            tmp.Quest       = 'Who is third party on a/c';
            tmp.Answer      = 'Click Here to view CFE';
            tmp.Link        = 'CFEPanel';
            tmp.UniqueId    = string.valueof(i);
            i++;                
            GroupOneQuestions.add(tmp);
        }
    }   

    system.debug('******* 13 = BuildGroup1Questions');
    if(!HelperMethods_v1.ValueIsNull(deal_summary)){
        if(deal_summary.size() > 0){
            Question tmp    = new Question();
            tmp.Quest       = 'How were funds paid into account (for Bond/ISA customers only)';
            tmp.Answer      = 'Click here to view Deal Summary';
            tmp.Link        = 'DealSummaryPanel';
            tmp.UniqueId    = string.valueof(i);
            i++;
            GroupOneQuestions.add(tmp);
        }
    }

    // system.debug('******* BuildGroup1Questions: call "getActualQuestions"');
    // getActualQuestions();

}

so at this point we have a list of a few questions to be asked of the customer, and 3 must be answered correctly in order to proceed.

this is displayed in our VF page like this:

<table border="" class="WizardTable"> 
    <tr>
        <td colspan="2" style="width:50%;" valign="top" align="left">
            <font class="headerBlue">Complete Group One Questions</font>
            <apex:actionStatus id="g1Status">
                <apex:facet name="start">
                    <apex:image value="{!$Resource.AjaxIcon}" height="20" width="20"/>
                </apex:facet>
            </apex:actionStatus>
        </td>
        <td colspan="2" style="width:50%;" valign="top" align="right">
            <font class="red">Must answer {!G1Target} correct</font>
        </td>
    </tr> 

    <apex:repeat var="g1" value="{!GroupOneQuestions}">

–>

    <tr>
        <!-- is this a personal or business account-->
        <td class="ColumnOne">
            <font class="MainPanelFont">{!g1.Quest}</font>                                      
        </td>

and it works, showing the questions as below:

enter image description here

So I thought I could generate a random index and pull 3 different random questions into my ActualQuestions list

private void getActualQuestions(){

    system.debug('******* getActualQuestions: GroupOneQuestions.size() = ' + GroupOneQuestions.size());
    if (GroupOneQuestions.size()>=4){


        Integer arrayLength = GroupOneQuestions.size(); 
        ActualQuestions = new list<Question>();

        // we need three random questions -- GroupOneQuestions.size()
        for (integer iDx=0; iDx<=3; iDx++){             
            system.debug('******* getActualQuestions: iDx loop value = ' + iDx);
            //Generate a random list index
            Double randomNumber = Math.random();

            Integer randomIndex = (randomNumber *(arrayLength-1)).intValue();
            system.debug('******* getActualQuestions: randomIndex = ' + randomIndex);
            Question tmp    = GroupOneQuestions[randomIndex];

            system.debug('******* getActualQuestions: current question = ' + tmp.Quest);
            ActualQuestions.add(tmp);
            system.debug('******* getActualQuestions: ActualQuestions.size() = ' + ActualQuestions.size());
            }           
        }

        system.debug('******* getActualQuestions: ActualQuestions.size() = ' + ActualQuestions.size());                     
    }

Unfortunately, I must have got my logic wrong, as sometimes it shows different questions, and sometimes it just shows the same question either 4 times, or repeated:

enter image description here

so what should I be doing to get 3 or 4 random questions from the list of questions in the List variable GroupOneQuestions?

Best Answer

Try using remove. If you want to preserve the original list you could clone it into a placeholder so you're removing elements from a different collection. Doing so makes your method idempotent, so it is probably a good idea. Also it might be simpler to just use a while loop.

public List<Question> getActualQuestions()
{
    if (groupOneQuestions.size() <= 3) return groupOneQuestions;
    List<Question> actuals = new List<Question>();
    List<Question> clones = groupOneQuestions.clone();
    while (actuals.size() < 3)
    {
        Integer index = (Math.random() * (clones.size() - 1)).intValue();
        actuals.add(clones.remove(index));
    }
    return actuals;
}