Difference between revisions of "SurveyCTO Dynamically Populated Choice Lists From Select One"

Jump to: navigation, search
Line 1: Line 1:
== Best Practice ==  
== Best Practice ==  
Sometimes we want to ask a respondent to select one or several answers out of answers the respondent have given earlier in the interview. For example, we might want to ask who in the household out of the household members listed in the household roster module is currently employed. It is possible to do this by dynamically load previous answers as answer options.
In almost every survey we have a roster where we select among categories, for example, a household lists the crops they grow or the sources of income they have. This is often done using a repeat group where each repeat has a select_one field where a crop or source of income is selected from a fixed choice list of possible crops or sources of income.  


Any answer can be used in dynamic choice lists, but when using variables inside a repeat group, one more extra step is required. We cannot reference a field inside a repeat group directly.
It is also common that we after the repeat group wants the respondent to answer questions like ''Which crop (one) did you grow the most?'' or ''Which sources (multiple) of income are new this year?''. And for these question we want to use the same choice list used in the repeat group but we only want to display the crops and sources of income that the respondent selected in the repeat group.  


== Coding Example ==
This coding example shows a simple solution for how to implement this.


Here is a [https://docs.google.com/spreadsheets/d/1X23Xg_RGSMEVBlcLtf7nOaHxjKDgxEB3y2Fi0vjOFUE/edit?usp=sharing code example] of how the answers to a field inside a repeat group are used to dynamically load the answer options for a select_one or a select_multiple question.
== Code Example ==
The key to this is that it is possible to take values from multiple fields and replicate the format SurveyCTO stores the result from a select_multiple, and then use select_multiple functions on that replicated list. Here is a [https://docs.google.com/spreadsheets/d/1X23Xg_RGSMEVBlcLtf7nOaHxjKDgxEB3y2Fi0vjOFUE/edit?usp=sharing code example] for the recommended implementation, and the text below explains some detail.


This example dynamically load answers from a repeat group. If you were to dynamically load answers from fields not inside a repeat group, then you simple reference those fields directly in the choice tab.
In this code example we use a simple crop roster and then ask the respondent to select the crop, out of the crops already selected, that he/she grew the most. Use this code example as a starting point and modify it to the specific requirements that you need.  


Not that this coding example can be improved in many ways. It has excluded some possible improvements in order to highlight the functionality discussed in this article. The most obvious improvement would be to filter the answer options so that only the answer options needed are displayed. See the section about choice filters.
To understand what is going on we start with a slightly simplified explanation on how ODK/SurveyCTO stores data in the tablet. A select_multiple is stored like this: '''1 3 12''' where each number is the code for each answer selected. If we have the individual values 1, 3 and 12 we can manually create this list. If the values are stored in the fields ''Field1'', ''Field2'' and ''Field3'', that each were of type select_one (see [[SurveyCTO_Dynamically_Populated_Choice_Lists_From_Select_One#Advanced_special_case|advanced special case]] if it is a different type), we can create the list with this code <code>concat(${field1}, ' ', ${field2}, ' ', ${field3})</code>. The <code>' '</code> part makes sure that each value is separated by a space. Using ''concat()'' quickly gets messy if the number of fields are more than a few. If the individual value comes from a repeated field, as in the crop roster example here, then there is an simpler method.
 
If each individual value comes from a repeat group then you can instead use  <code>join(' ' , ${field})</code> where ''field'' is the name of the select_one field in the repeat group with the individual values, and <code>' '</code> is the symbol (a space) that should separate the values. Using ''join()'' or ''concat()'' the way described here you get a list on the format '''1 3 12'''. On this list you can use functions like ''selected()'' and we can use ''selected()'' in a filter expression to display only the choice options in this list. While ''selected()'' is meant to be used on list created by select_multiple fields, there is nothing stopping us to use that function on lists we created manually as long as the format is identical.
 
In the code example linked to above, the field crop in the repeat group use the same choice list as the field crop_most. This is important for the simplicity of the filter we use. For crop_most the filter expression only display the choice options for which the value in the filter column in the choice tab that is in the list created by ''join()''. If the question would be different it is perfectly possible to change crop_most to be a select_multiple.
 
Note that in the code example, a very similar filter is used to prevent that the same crop is selected twice in the repeat group. By using the ''not()'' function we get the exact opposite filter than the filter we use for crop_most. This is a neat usage of this method on its own.
 
=== Advanced special case ===
If the individual values does not come from select_one field but text fields then it is possible to combine this method with the method where the the choice lists are [[SurveyCTO Dynamically Populated Choice Lists|dynamically created from text fields]]. If you are an advanced SurveyCTO user you can try this yourself, otherwise let us know and we will create an example if there is enough demand.


== Back to Parent ==
== Back to Parent ==

Revision as of 10:57, 28 December 2017

Best Practice

In almost every survey we have a roster where we select among categories, for example, a household lists the crops they grow or the sources of income they have. This is often done using a repeat group where each repeat has a select_one field where a crop or source of income is selected from a fixed choice list of possible crops or sources of income.

It is also common that we after the repeat group wants the respondent to answer questions like Which crop (one) did you grow the most? or Which sources (multiple) of income are new this year?. And for these question we want to use the same choice list used in the repeat group but we only want to display the crops and sources of income that the respondent selected in the repeat group.

This coding example shows a simple solution for how to implement this.

Code Example

The key to this is that it is possible to take values from multiple fields and replicate the format SurveyCTO stores the result from a select_multiple, and then use select_multiple functions on that replicated list. Here is a code example for the recommended implementation, and the text below explains some detail.

In this code example we use a simple crop roster and then ask the respondent to select the crop, out of the crops already selected, that he/she grew the most. Use this code example as a starting point and modify it to the specific requirements that you need.

To understand what is going on we start with a slightly simplified explanation on how ODK/SurveyCTO stores data in the tablet. A select_multiple is stored like this: 1 3 12 where each number is the code for each answer selected. If we have the individual values 1, 3 and 12 we can manually create this list. If the values are stored in the fields Field1, Field2 and Field3, that each were of type select_one (see advanced special case if it is a different type), we can create the list with this code concat(${field1}, ' ', ${field2}, ' ', ${field3}). The ' ' part makes sure that each value is separated by a space. Using concat() quickly gets messy if the number of fields are more than a few. If the individual value comes from a repeated field, as in the crop roster example here, then there is an simpler method.

If each individual value comes from a repeat group then you can instead use join(' ' , ${field}) where field is the name of the select_one field in the repeat group with the individual values, and ' ' is the symbol (a space) that should separate the values. Using join() or concat() the way described here you get a list on the format 1 3 12. On this list you can use functions like selected() and we can use selected() in a filter expression to display only the choice options in this list. While selected() is meant to be used on list created by select_multiple fields, there is nothing stopping us to use that function on lists we created manually as long as the format is identical.

In the code example linked to above, the field crop in the repeat group use the same choice list as the field crop_most. This is important for the simplicity of the filter we use. For crop_most the filter expression only display the choice options for which the value in the filter column in the choice tab that is in the list created by join(). If the question would be different it is perfectly possible to change crop_most to be a select_multiple.

Note that in the code example, a very similar filter is used to prevent that the same crop is selected twice in the repeat group. By using the not() function we get the exact opposite filter than the filter we use for crop_most. This is a neat usage of this method on its own.

Advanced special case

If the individual values does not come from select_one field but text fields then it is possible to combine this method with the method where the the choice lists are dynamically created from text fields. If you are an advanced SurveyCTO user you can try this yourself, otherwise let us know and we will create an example if there is enough demand.

Back to Parent

This article is part of the topic SurveyCTO Coding Practices