Kind of a follow-up to this post
I have a list describing the contents of someone's closet. I'm taking inventory of this closet while I fill out this survey, but I really only care about what colors are represented in the assemblage. So I want to know what color shirts are in the closet, regardless of what style they are.
Example table (pivoted for readability)
Item Type | Item Style | Colors possible in that Style |
Shirt | Collared | Blue (Navy), Blue (Pale), Green, Red |
Henley | Blue (Pale), Grey, White | |
T-Shirt | Green, Grey, Purple, Red | |
Tank-top | Black, Blue (Navy), Grey, Green, Purple |
If someone selects a Collared shirt and Tank-top, the full set of choices will appear.
Item Style | Colors possible |
Collared, Tank-top | Blue (Navy), Black, Blue (Pale), Blue (Navy), Green, Grey, Red, Green, Purple |
(Survey123 puts the choices as A[0], B[0], A[1], B[1], etc...)
Since I only care about the color of his entire collection of shirts, how can I make a clean list of unique choices appear?
Collared, Tank-top | Black, Blue (Navy), Blue (Pale), Green, Grey, Purple, Red |
(If this were really what the survey was, I would cascade in reverse, starting with colors, then going to Styles, but that's not possible with my actual dataset.)
Thanks!
Solved! Go to Solution.
Okay, I have a working solution.
The code to generate the if() statements is
function choicenum(N) {
var index_range = [];
//Create an index range based on the count-selected in the question.
for (var i = 1; i <= N; i++) {
index_range.push(i);
}
// Create your filter statements
var filterstr = []
for (var y in index_range) {
var midstr = []
midstr.push("if(count-selected(${Animal})=" + (Number(y)+1) + ", ");
// Create a sub-index for the second clause of the if()
var subindex= []
for (var f = 0; f <= y; f++) {
subindex.push(f);
midstr.push("contains(Animal, selected-at(${Animal}, '" + f + "' )) or ")
}
// Concatenates into a string, joined by a dummy character
var split_char= "?"
midstr = midstr.join(split_char);
// splits along the dummy character and stitches back together
midstr= midstr.split(split_char).join('');
// Removes the last "or" from the string
midstr = midstr.substring(0, midstr.length -4)
filterstr.push(midstr);
}
filterstr = filterstr + ", \"\"" +")".repeat(N)
return filterstr
}
//Outcome for N=2
//if(count-selected(${Animal})=1, contains(Animal, selected-at(${Animal}, '0' )),
//if(count-selected(${Animal})=2, contains(Animal, selected-at(${Animal}, '0' ))
//or contains(Animal, selected-at(${Animal}, '1' )), ""))
console.log(choicenum(2))
It's not the ideal solution; originally I had created a script that worked dynamically. Unfortunately, S123 can't use formulas that are the result of a pulldata() or are the values in another question as filter expressions; it instead tries to filter based off the string of the expression.
Have you looked at the allow duplicate choice option? Not sure but it may filter for you.
Thanks for the response.
I tried setting it to "no" and leaving it blank, and both had the same result as leaving it as yes, which was the default.
I think that setting is supposed to be if you want your choice to show up under multiple filters, e.g. If the previous answer to the filter question was "Shirt", rather than having your choice show up after selecting multiple (e.g. "Shirt" and "Pants").
Another thing that might be adding a wrinkle to this is that Iʻm referencing an external csv for this question, so maybe that changes whether or not the setting applies?
Update 9/7/2021
I'm having some limited success by placing the relevant items for each color into one cell, then using
contains(Animal, selected-at(${Animal}, '0') or contains(Animal, selected-at(${Animal}, '1') or ...
Sorry, I had to change dummy data
The problem is that it won't kick in until the number of choices that are enumerated in this choice filter are selected. So if I go all the way up to selected index 3, nothing will filter until I have 4 choices selected.
I'm now trying to figure out how to make it not tied to the number of selections and could use some suggestions.
Thanks again!
Example photos:
Here is my choice list:
With Index up to index 1,
No Choices:
One Choice:
Two Choices selected:
Update 9/8/2021
Good news! I have gotten it to work.
Bad news: It is extremely tedious to set up.
Using the data I shared yesterday, this code will work for all of the options.
if(count-selected(${Animal})=1, contains(Animal, selected-at(${Animal}, '0')),
if(count-selected(${Animal})=2, (contains(Animal, selected-at(${Animal}, '0')) or contains(Animal, selected-at(${Animal}, '1'))),
if(count-selected(${Animal})=3, (contains(Animal, selected-at(${Animal}, '0')) or contains(Animal, selected-at(${Animal}, '1')) or contains(Animal, selected-at(${Animal}, '2'))),
if(count-selected(${Animal})=4, (contains(Animal, selected-at(${Animal}, '0')) or contains(Animal, selected-at(${Animal}, '1')) or contains(Animal, selected-at(${Animal}, '2')) or contains(Animal, selected-at(${Animal}, '3'))),
if(count-selected(${Animal})=5, (contains(Animal, selected-at(${Animal}, '0')) or contains(Animal, selected-at(${Animal}, '1')) or contains(Animal, selected-at(${Animal}, '2')) or contains(Animal, selected-at(${Animal}, '3')) or contains(Animal, selected-at(${Animal}, '4'))),"")))))
You can't actually put line breaks in the calculation field, or else it breaks the formula, so in real life it's even harder to read than this.
I'm hoping that there's a way to have a javascript script evaluate the number of selected choices and then build the filter statement from there. My real life data is upwards of 80 choices to deal with (even after paring it down with other filters first), so writing this out for that many possibilities is not ideal.
I have a rough idea of what do, but don't know how to code well enough to make it happen.
"if(count-selected(${Animal})=", X, ", "
"contains(Animal, selected-at(${Animal},'", (Y-1), "'))", " or""
"), "")"
Specifically what I don't know how to do is creating a range, and then getting rid of the last "or".
Thanks!
Okay, I have a working solution.
The code to generate the if() statements is
function choicenum(N) {
var index_range = [];
//Create an index range based on the count-selected in the question.
for (var i = 1; i <= N; i++) {
index_range.push(i);
}
// Create your filter statements
var filterstr = []
for (var y in index_range) {
var midstr = []
midstr.push("if(count-selected(${Animal})=" + (Number(y)+1) + ", ");
// Create a sub-index for the second clause of the if()
var subindex= []
for (var f = 0; f <= y; f++) {
subindex.push(f);
midstr.push("contains(Animal, selected-at(${Animal}, '" + f + "' )) or ")
}
// Concatenates into a string, joined by a dummy character
var split_char= "?"
midstr = midstr.join(split_char);
// splits along the dummy character and stitches back together
midstr= midstr.split(split_char).join('');
// Removes the last "or" from the string
midstr = midstr.substring(0, midstr.length -4)
filterstr.push(midstr);
}
filterstr = filterstr + ", \"\"" +")".repeat(N)
return filterstr
}
//Outcome for N=2
//if(count-selected(${Animal})=1, contains(Animal, selected-at(${Animal}, '0' )),
//if(count-selected(${Animal})=2, contains(Animal, selected-at(${Animal}, '0' ))
//or contains(Animal, selected-at(${Animal}, '1' )), ""))
console.log(choicenum(2))
It's not the ideal solution; originally I had created a script that worked dynamically. Unfortunately, S123 can't use formulas that are the result of a pulldata() or are the values in another question as filter expressions; it instead tries to filter based off the string of the expression.