setvalueSet, extracting from multiple choice question

Discussions about CSEntry
Charles Asabere
Posts: 2
Joined: August 15th, 2015, 5:52 pm

setvalueSet, extracting from multiple choice question

Post by Charles Asabere »

crop.zip
(5.63 KiB) Downloaded 437 times
Good day everyone.

I'm New to this forum.
I am facing a challenge now, I have 3 multiple choice questions eg. Q1,Q2,Q3. However, options for Q2 should come from options not selected from Q1 and options for Q3' should be a combined options selected from both Q1 and Q2. For example,

Q1 "Crops planted in 2014, record Spontaneous response provided by farmer .."
1. Maize
2. Rice
3. Soya Bean
4. Millet
5. Sorghum
6. Cowpea
and the farmer selects "1 Maize", "4 Millet" .

in Q2. "Did you ever planted any of these in 2014"?, Q2 should only bring options that were not selected in Q1.
2. Rice
3. SoyaBean
5. Sorghum
6. Cowpea.
So lets say the farmer selects "2 Rice" and "6 Cowpea"

now Q3 "Which of these do you intend planting next year."
Q3 should only bring for the respondent to choose from
1. Maize
2. Rice
4. Millet
6. Cowpea.


How do I do this with the SetValueSet().
A little help will be greatly appreciated.
Thanks.
josh
Posts: 2399
Joined: May 5th, 2014, 12:49 pm
Location: Washington DC

Re: setvalueSet, extracting from multiple choice question

Post by josh »

Welcome to the forum. For a bit of background on how to interpret checkboxes using the pos function you may want to see this post (http://csprousers.org/forum/viewtopic.p ... 9&start=10).

To use setvalueset to implement the crops questions that you describe you will need to declare two string arrays in the proc global. With most setvalueset examples you use a numeric array of codes and a string array of labels but since you are dealing with checkboxes your codes array will be a string array too.

In the onfocus of Q2 you will need to loop through each of the possible crop codes (1 through 6) and check if the corresponding crop was chosen in Q1. If it was NOT chosen then you add it to the codes and labels arrays. After the loop you call setvalueset on Q2 with the codes and labels arrays. What makes this a bit tricky is that since Q1 and Q2 are alphanumeric variables, the codes are not the numeric values 1 through 6. They are the string equivalents: "1", "2", ... "6". So in your loop you will need to convert from the number to the equivalent string. You can do this with either the maketext function or the edit function. Once you have the codes as strings then you can use pos to determine if the code was selected in Q1. If it is selected then you add it to the array of codes and labels.

Here is how I would write the loop:
PROC Q2
onfocus
// Setvalueset for Q2 to crops not selected in Q1

// Numeric crop code (1-6)
numeric codeNumeric;

// Next slot in codes/labels array to insert into
numeric nextEntry = 1;

// Loop through each crop
do codeNumeric = 1 while codeNumeric <= 6

    
// String version of crop code ("1" instead of 1)
    string codeString = edit("9", codeNumeric);
    
    
// Check if crop was selected in Q1
    if pos(codeString, Q1) = 0 then
        
// Not selected in Q1, add to value set for Q2
        codes(nextEntry) = codeString;
        labels(nextEntry) =
getlabel(Q1, codeString);
        
        
// Filled in last slot, point to next empty slot
        nextEntry = nextEntry + 1;
    
endif;
enddo;
// Mark end of new value set
codes(nextEntry) = "";

// Set value set for Q2
setvalueset(Q2, codes, labels);
For Q3 the logic would be similar except that instead of testing if the code was not selected in Q1 you would check to see if it was selected in either Q1 or Q3.

You should also decide how to handle the case where all the crops are selected in Q1 - do you skip Q2? Same thing if nothing was selected in either Q1 or Q2 - do you skip Q3?
Charles Asabere
Posts: 2
Joined: August 15th, 2015, 5:52 pm

Re: setvalueSet, extracting from multiple choice question

Post by Charles Asabere »

Thanks Josh, It works great.

But what if the options span more than 12 options, and in that case we have to introduce alphabets e.g "1, 2, 3, 4, .....9, A, B, C, D, E".

would "string codeString = edit("9", codeNumeric);" still hold as we now have alphabets in them.

Thanks.
josh
Posts: 2399
Joined: May 5th, 2014, 12:49 pm
Location: Washington DC

Re: setvalueSet, extracting from multiple choice question

Post by josh »

There are two ways to handle the case where you have more options than you can represent with the numbers 0 through 9:

1) Use letters
As you suggest your codes can include letters (1,2,3,4,5,6,7,8,9,0,A,B,C...). In this solution instead of using edit() to convert from number to string you use an alphabet string to do the conversion. Something like this:
string alphabet = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";

// Get substring of length 1 at position codeNumeric of alphabet string
string codeString = alphabet[codeNumeric:1];
2) Use two digits
In this solution your codes are still numbers (represented as strings) but you use 2 digits so you have 1,2,3,4,5,6,7,8,9,10,11,12... You can still use edit to convert from numbers to string but you must change the first argument to edit to handle the additional digit.
string codeString = edit("99", codeNumeric);
In addition, to test whether a 2 digit code is in the string you can't just compare pos() to zero since you could have a case like pos("01", "1011") which would return 1 even though only options "10" and "11" were selected, not option "01". To avoid this, I usually define my own function that is similar to pos but only looks for the codes on odd numbered positions for 2 digit codes. For three digit codes you need to look only at every third position. Generalizing this to any length code I use the following function:
// Determine if code was checked in checkbox field checkboxItem.
// Supports any multiple digit codes. Steps through
// the field by the length of the code and checks for the
// presence of code in each position. For 1 digit codes checks
// every position (just like pos), for 2 digit codes checks positions
// 1,3,5... and for 3 digit codes checks 1,4,7... This avoids
// false positives that you would get using pos in a case like
// pos("01", "1011").
function isChecked(string code, string checkboxItem)
    
numeric i;
    
    
// Step by length of code so that we don't look at substrings
    // that are not aligned to code boundary.
    numeric stepSize = length(code);
    
    
// Assume not checked, this will be return value if we
    // find nothing.
    isChecked = 0;
    
    
// Loop through checkboxField
    do i = 1 while i <= length(checkboxItem) by stepSize
    
        
// Check substring of length stepSize at position i
        if code = checkboxItem[i:stepSize] then
            
// Found a match, set return value to true and exit loop
            isChecked = 1;
            
break;
        
endif;
    
enddo;
end;
Guest

Re: setvalueSet, extracting from multiple choice question

Post by Guest »

Thank you very much.

And thanks for the insight on the user defined function as well.
Mike010
Posts: 3
Joined: January 25th, 2016, 4:45 pm

Re: setvalueSet, extracting from multiple choice question

Post by Mike010 »

Hi Josh:

Have you any example whit this function?

Thanks
josh
Posts: 2399
Joined: May 5th, 2014, 12:49 pm
Location: Washington DC

Re: setvalueSet, extracting from multiple choice question

Post by josh »

You could use this function in place of pos() in the example above so just take that example and replace pos() with isChecked().
yanina
Posts: 60
Joined: October 31st, 2016, 9:37 am

Re: setvalueSet, extracting from multiple choice question

Post by yanina »

Dear Josh

I have been trying to follow your guide to use two digits number for multiple responses but still confused where to put the logic that you post above :cry: :cry: . Two digit value such 10,11 .. 14 failed to recode as 1 or 0 on Q1Y on sub 10,11 ..

I have a program:
Q1X: String :
Q1Y Roster with 14 Occurence. Protected roster.

I attached test.zip and its screenshoot, would you please see it.

Thanks Josh. You are very kind.

Yanin

This is my logic :

Code: Select all

PROC GLOBAL

PROC TEST_FF
PROC Q1X
numeric i;
string alphabet = "0123456789";

do i = 1 while i <= 14 

string letter = alphabet[i:1]; 

if pos(letter, $) <> 0 then 
Q1Y (i) = 1; else Q1Y (i) = 0; 
endif;	

enddo;
if length(strip($)) = 0 then 
endlevel; 
endif; // Skip rest if none produced

PROC Q1Y000
Preproc
set attributes ($) protect;
Attachments
TEST.zip
(2.62 KiB) Downloaded 359 times
2DIGIT.PNG
2DIGIT.PNG (25.5 KiB) Viewed 8439 times
josh
Posts: 2399
Joined: May 5th, 2014, 12:49 pm
Location: Washington DC

Re: setvalueSet, extracting from multiple choice question

Post by josh »

Copy and paste the function isChecked above into your proc global. Then in your PROC Q1X replace the loop with:
do i = 1 while i <= 14
    
string codeString = edit("99", i);
    
if isChecked(codeString, $) <> 0 then
        Q1Y (i) =
1;
    
else
        Q1Y (i) =
0;
    
endif;   
enddo;
yanina
Posts: 60
Joined: October 31st, 2016, 9:37 am

Re: setvalueSet, extracting from multiple choice question

Post by yanina »

Perfecto Josh. The logic is working fine like a charm.

Very very very big thanks.

Yanin

Code: Select all

=======
{Application 'TEST' logic file generated by CSPro}
PROC GLOBAL

///
// Determine if code was checked in checkbox field checkboxItem.
// Supports any multiple digit codes. Steps through
// the field by the length of the code and checks for the
// presence of code in each position. For 1 digit codes checks
// every position (just like pos), for 2 digit codes checks positions
// 1,3,5... and for 3 digit codes checks 1,4,7... This avoids
// false positives that you would get using pos in a case like
// pos("01", "1011").
function isChecked(string code, string checkboxItem)
    numeric i;
    
    // Step by length of code so that we don't look at substrings
    // that are not aligned to code boundary.
    numeric stepSize = length(code);
    
    // Assume not checked, this will be return value if we
    // find nothing.
    isChecked = 0;
    
    // Loop through checkboxField
    do i = 1 while i <= length(checkboxItem) by stepSize
    
        // Check substring of length stepSize at position i
        if code = checkboxItem[i:stepSize] then
            // Found a match, set return value to true and exit loop
            isChecked = 1;
            break;
        endif;
    enddo;
end;
///

PROC TEST_FF

PROC Q1X

numeric i;
string alphabet = "0123456789";

do i = 1 while i <= 14
    string codeString = edit("99", i);
    if isChecked(codeString, $) <> 0 then 
        Q1Y (i) = 1;
    else 
        Q1Y (i) = 0; 
    endif;   
enddo;

PROC Q1Y000
Preproc
set attributes ($) protect;

=======
Post Reply