jQuery - How to separate two objects which are dep

2019-05-31 19:17发布

问题:

I'm trying to make generic version of a form where dropdown would appear depending on previous dropdown value, but some dropdown is dependent on two or more previous answers.

The problem I encountered is that got questions which are dependent on the same question with same answer, so when I iterate JSON, it shows them both, meanwhile second question is suppose to appear only when all dependent answers are fulfilled, so I need a way to separate them. Currently, questions with Id 8 and 9 have same dependent answers, but question 9 has one more dependency.

JSON looks like this:

var questions = [
         //questions before these  
        {

            Id: 6,
            ProductGroups: [{
                    ProductGroupId: 1,
                    show: false
                },
                {
                    ProductGroupId: 2,
                    show: true
                }
            ],
            //dependant answer(s)
            DependantAnswers: [{
                QuestionId: 1,
                answer: ""
            }]
        },
        {

            Id: 7, //guid
            ProductGroups: [{
                    ProductGroupId: 1,
                    show: false
                },
                {
                    ProductGroupId: 2,
                    show: false
                }
            ],
            //dependant answer(s)
            DependantAnswers: [{
                QuestionId: 6,
                answer: "male"
            }]
        },
        {

            Id: 8, //guid
            ProductGroups: [{
                    ProductGroupId: 1,
                    show: false
                },
                {
                    ProductGroupId: 2,
                    show: false
                }
            ],
            //dependant answer(s)
            DependantAnswers: [{
                QuestionId: 6,
                answer: "female"
            }
            ]
        },
        {

            Id: 9, //guid
            ProductGroups: [{
                    ProductGroupId: 1,
                    show: false
                },
                {
                    ProductGroupId: 2,
                    show: false
                }
            ],
            //dependant answer(s)
            DependantAnswers: [{
                    QuestionId: 6,
                    answer: "female"
                },
                {
                    QuestionId: 8,
                    answer: "yes"
                }
            ]
        }

    ];

And this is jQuery function:

function onQuestionSelectChange() {
        $("#questionsContainer div select").change(function () {

            var selectedValue = $(this).val();
            var selectedQuestion = $(this).parent().attr('id').split('-');
            var selectedQuestionId = selectedQuestion[1];

            var potentialQuestions = [];
            //var filteredQuestions = [];


            $.each(questions, function(index, element) {
                $.each(element.DependantAnswers, function (indexDepAnswer, elemDepAnswer){
                    if(elemDepAnswer.answer == selectedValue)
                        potentialQuestions.push(element);

                });

            });
            //here I need to separate question 8 from 9 and show only question 8

        });

    }

With above code I get an array of 2 objects filled with question 8 and 9, but question 9 needs to appear only when question 8 is answered with value "yes". Whatever "if" I tried, question 9 passes just like question 8, because it has same dependent answers. How can I filter question 9 and show it only after I picked "yes" on question 8?

回答1:

The issue with your current solution is that you are checking the current selected answer in the array of questions, and if you find the current selected answer to be in the list of dependent answers in any question, then you are considering that question as the potential next question, even though that question may have more than one depedency which is may not be fulfilled but you can not check them since you are not maintaining any kind of state.

One thing that you can do create a hashmap which simply keeps track of the questions which has been answered and while checking for next potential question lookup if all the dependcy is fulfilled.

So your original function which I'm assuming is being called on each select change.

var questionsCompleted = Object.create(null);

function onQuestionSelectChange() {
    $("#questionsContainer div select").change(function() {

        var selectedValue = $(this).val();
        var selectedQuestion = $(this).parent().attr('id').split('-');
        var selectedQuestionId = selectedQuestion[1];

        var potentialQuestions = [];
        //var filteredQuestions = [];

        // Update the Hashmap 
        questionsCompleted[selectedQuestionId] = selectedValue;

        // Check all the questions for the next potential question. The next potential question will be the one whose all dependent question are already answered
        $.each(questions, function(index, element) {
            var fulfilled = 0;
            $.each(element.DependantAnswers, function(indexDepAnswer, elemDepAnswer) {
                if (elemDepAnswer.answer === questionsCompleted[element.Id])
                    fulfilled++;

            });
            if (fulfilled === element.DependantAnswers.length) // All dependency fulfilled
                potentialQuestions.push(element);

        });
        //here I need to separate question 8 from 9 and show only question 8

    });

} 

Hope this helps.



回答2:

This is one way of doing it, where you have an array of already answered questions so that you can keep track of which potentialQuestions that are valid to show. I'm sure you can optimize this, but this one is to show how to do it in a fairly simple way. Hope this helps.

let selectedValue = 'female';
// Push selectedValue into some array to keep track of current answers
let selectedValues = [
  {QuestionId: 1, answer: 'q1'},
  {QuestionId: 2, answer: 'q2'},
  {QuestionId: 3, answer: 'q3'},
  {QuestionId: 4, answer: 'q4'},
  {QuestionId: 5, answer: 'q5'},
  {QuestionId: 6, answer: 'female'}
];

let resultingQuestions = [];

questions.filter((q) => {
  return q.DependantAnswers.find((dp) => { 
    return dp.answer === selectedValue;
  });
}).filter((pq) => {
  let validQuestion;
  pq.DependantAnswers.forEach((da) => {
    validQuestion = selectedValues.find((sv) => {
      return sv.QuestionId === da.QuestionId && sv.answer === da.answer;
    }); 
  });

  if (validQuestion) {
    resultingQuestions.push(pq);
  }
});