Need Help with Javascript to Conduct Conjoint Experiment Including Randomly-Selected Features | XM Community
Skip to main content

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]]); 
}

Be the first to reply!

Leave a Reply