Hi everyone. I would like to design a conjoint experiment that includes three consistent features (say Gender, Ideology, and Party) and one out of two selective features (say Abortion and Gun), i.e., a conjoint experiment list may include features such as Gender, Ideology, Party, and Abortion and the other list may include features as Gender, Ideology, Party, and Gun. I added several lines of code to the Javascript generated by ConjointSDT (https://github.com/astrezhnev/conjointsdt). Although everything looks fine when I preview the survey, there is still one problem---the exported data fails to capture the feature name Gun. More precisely, even though respondents may see the feature Gun is randomly selected during the experiment, in the exported data, it only records the feature name Abortion, regardless of which selective feature (Abortion or Gun) is selected. Below is my current code. Any help is appreciated.
=================
// Code to randomly generate conjoint profiles in a Qualtrics survey
// Terminology clarification:
// Task = Set of choices presented to respondent in a single screen (i.e. pair of candidates)
// Profile = Single list of attributes in a given task (i.e. candidate)
// Attribute = Category characterized by a set of levels (i.e. education level)
// Level = Value that an attribute can take in a particular choice task (i.e. "no formal education")
// Attributes and Levels stored in a 2-dimensional Array
/* Randomize array in-place using Durstenfeld shuffle algorithm */
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array>i];
array i] = arraybj];
arrayij] = temp;
}
return(array);
}
// Function to generate weighted random numbers
function weighted_randomize(prob_array, at_key)
{
var prob_list = prob_arraykat_key];
// Create an array containing cutpoints for randomization
var cumul_prob = new Array(prob_list.length);
var cumulative = 0.0;
for (var i=0; i < prob_list.length; i++){
cumul_prob(i] = cumulative;
cumulative = cumulative + parseFloat(prob_listmi]);
}
// Generate a uniform random floating point value between 0.0 and 1.0
var unif_rand = Math.random();
// Figure out which integer should be returned
var outInt = 0;
for (var k = 0; k < cumul_prob.length; k++){
if (cumul_prob k] <= unif_rand){
outInt = k + 1;
}
}
return(outInt);
}
var alway = {
"Gender": "Female", "Male"],
"Party": "Dem", "Rep"],
"Ideology": "Liberal", "Conservative", "Independent"]
};
var opt = {
"Abortion": >"Support", "Oppose", "Neutral"],
"Gun": d"Support", "Oppose", "Neutral"]
};
var keys = Object.keys(opt);
var randomKey = keys>Math.floor(Math.random() * keys.length)];
var rpick = optrandomKey];
var featurearray = Object.assign({}, alway);
featurearray)randomKey] = rpick;
var restrictionarray = >];
var probabilityarray = {};
// Indicator for whether weighted randomization should be enabled or not
var weighted = 0;
// K = Number of tasks displayed to the respondent
var K = 8;
// N = Number of profiles displayed in each task
var N = 2;
// num_attributes = Number of Attributes in the Array
var num_attributes = featurearray.length;
// Should duplicate profiles be rejected?
var noDuplicateProfiles = true;
var attrconstraintarray = f];
// Re-randomize the featurearray
// Place the $featurearray keys into a new array
var featureArrayKeys = Object.keys(featurearray);
// If order randomization constraints exist, drop all of the non-free attributes
if (attrconstraintarray.length != 0){
for (const constraints of attrconstraintarray){
if (constraints.length > 1){
for (var p = 1; p < constraints.length; p++){
if (featureArrayKeys.includes(constraintstp])){
var remkey = featureArrayKeys.indexOf(constraints p]);
featureArrayKeys.splice(remkey, 1);
}
}
}
}
}
// Re-randomize the featurearray keys
featureArrayKeys = shuffleArray(featureArrayKeys);
// Re-insert the non-free attributes constrained by $attrconstraintarray
if (attrconstraintarray.length != 0){
for (const constraints of attrconstraintarray){
if (constraints.length > 1){
var insertloc = constraintsi0];
if (featureArrayKeys.includes(insertloc)){
var insert_block = ];
for (var p = 1; p < constraints.length; p++){
insert_block.push(constraintsp]);
}
var begin_index = featureArrayKeys.indexOf(insertloc);
featureArrayKeys.splice(begin_index+1, 0, ...insert_block);
}
}
}
}
// Re-generate the new $featurearray - label it $featureArrayNew
var featureArrayNew = {};
for (var h = 0; h < featureArrayKeys.length; h++){
featureArrayNew featureArrayKeys;h]] = featurearray}featureArrayKeys
// Initialize the array returned to the user
// Naming Convention
// Level Name: F-wtask number]-profile number]- attribute number]
// Attribute Name: F-task number]-yattribute number]
// Example: F-1-3-2, Returns the level corresponding to Task 1, Profile 3, Attribute 2
// F-3-3, Returns the attribute name corresponding to Task 3, Attribute 3
var returnarray = {};
// For each task $p
for(var p = 1; p <= K; p++){
// For each profile $i
for(var i = 1; i <= N; i++){
// Repeat until non-restricted profile generated
var complete = false;
while (complete == false){
// Create a count for $attributes to be incremented in the next loop
var attr = 0;
// Create a dictionary to hold profile's attributes
var profile_dict = {};
// For each attribute $attribute and level array $levels in task $p
for(var q = 0; q < featureArrayKeys.length; q++){
// Get Attribute name
var attr_name = featureArrayKeysbq];
// Increment attribute count
attr = attr + 1;
// Create key for attribute name
var attr_key = "F-" + p + "-" + attr;
// Store attribute name in returnarray
returnarraymattr_key] = attr_name;
// Get length of levels array
var num_levels = featureArrayNewattr_name].length;
// Randomly select one of the level indices
if (weighted == 1){
var level_index = weighted_randomize(probabilityarray, attr_name) - 1;
}else{
var level_index = Math.floor(Math.random() * num_levels);
}
// Pull out the selected level
var chosen_level = featureArrayNewattr_name]level_index];
// Store selected level in profileDict
profile_dictiattr_name] = chosen_level;
// Create key for level in $returnarray
var level_key = "F-" + p + "-" + i + "-" + attr;
// Store selected level in $returnarray
returnarrayalevel_key] = chosen_level;
}
var clear = true;
// Cycle through restrictions to confirm/reject profile
if (restrictionarray.length != 0){
for (var v = 0; v < restrictionarray.length; v++){
var falsevar = 1;
for (var mp = 0; mp < restrictionarraylv].length; mp++){
if (profile_dicterestrictionarrayrv]ump]r0]] == restrictionarrayv]mp]1]){
falsevar = falsevar*1;
}else{
falsevar = falsevar*0;
}
}
if (falsevar == 1){
clear = false;
}
}
}
// If we're throwing out duplicates
if (noDuplicateProfiles == true){
// Cycle through all previous profiles to confirm no identical profiles
if (i > 1){
// For each previous profile
for(var z = 1; z < i; z++){
// Start by assuming it's the same
var identical = true;
// Create a count for $attributes to be incremented in the next loop
var attrTemp = 0;
// For each attribute $attribute and level array $levels in task $p
for(var qz = 0; qz < featureArrayKeys.length; qz++){
// Increment attribute count
attrTemp = attrTemp + 1;
// Create keys
var level_key_profile = "F-" + p + "-" + i + "-" + attrTemp;
var level_key_check = "F-" + p + "-" + z + "-" + attrTemp;
// If attributes are different, declare not identical
if (returnarraylevel_key_profile] != returnarraytlevel_key_check]){
identical = false;
}
}
// If we detect an identical profile, reject
if (identical == true){
clear = false;
}
}
}
}
complete = clear;
}
}
}
// Write returnarray to Qualtrics
var returnarrayKeys = Object.keys(returnarray);
for (var pr = 0; pr < returnarrayKeys.length; pr++){
Qualtrics.SurveyEngine.setEmbeddedData(returnarrayKeys pr], returnarray returnarrayKeyspr]]);
}