SurveyCTO Dynamically Populated Choice Lists From Select One

Revision as of 11:27, 5 April 2018 by Admin (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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