setvalueSet, extracting from multiple choice question
-
- Posts: 2
- Joined: August 15th, 2015, 5:52 pm
setvalueSet, extracting from multiple choice question
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.
Re: setvalueSet, extracting from multiple choice question
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:
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?
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.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);
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?
-
- Posts: 2
- Joined: August 15th, 2015, 5:52 pm
Re: setvalueSet, extracting from multiple choice question
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.
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.
Re: setvalueSet, extracting from multiple choice question
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:
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.
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// Get substring of length 1 at position codeNumeric of alphabet string
string codeString = alphabet[codeNumeric:1];
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;
// 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;
Re: setvalueSet, extracting from multiple choice question
Thank you very much.
And thanks for the insight on the user defined function as well.
And thanks for the insight on the user defined function as well.
Re: setvalueSet, extracting from multiple choice question
Hi Josh:
Have you any example whit this function?
Thanks
Have you any example whit this function?
Thanks
Re: setvalueSet, extracting from multiple choice question
You could use this function in place of pos() in the example above so just take that example and replace pos() with isChecked().
Re: setvalueSet, extracting from multiple choice question
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 . 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 :
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 . 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 (25.5 KiB) Viewed 8439 times
Re: setvalueSet, extracting from multiple choice question
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;
string codeString = edit("99", i);
if isChecked(codeString, $) <> 0 then
Q1Y (i) = 1;
else
Q1Y (i) = 0;
endif;
enddo;
Re: setvalueSet, extracting from multiple choice question
Perfecto Josh. The logic is working fine like a charm.
Very very very big thanks.
Yanin
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;
=======