我已经构建了一些程序来查找用户可以在问卷中输入的内容的可能路径。然而,逻辑需要稍微改变,以便能够在跳过问题的情况下找到可能的路径。
当前解决方案:
const originalQuestions = {
1: {
title: "Title",
firstQuestion: true,
options: [{
tooltip: "",
nextQuestion: 2
}, {
tooltip: "",
nextQuestion: 2
}, {
tooltip: "",
nextQuestion: 10000
}]
},
2: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 3
}, {
tooltip: "",
nextQuestion: 3
}, {
tooltip: "",
nextQuestion: 3
}, {
tooltip: "",
nextQuestion: 3
}]
},
3: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 4
}, {
tooltip: "",
nextQuestion: 4
}, {
tooltip: "",
nextQuestion: 4
}]
},
4: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 13
}, {
tooltip: "",
nextQuestion: 5
}]
},
5: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 6
}, {
tooltip: "",
nextQuestion: 6
}, {
tooltip: "",
nextQuestion: 6
}, {
tooltip: "",
nextQuestion: 10000
}]
},
6: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 14
}]
},
7: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 17
}, {
tooltip: "",
nextQuestion: 17
}, {
tooltip: "",
nextQuestion: 17
}, {
tooltip: "",
nextQuestion: 17
}, {
tooltip: "",
nextQuestion: 17
}]
},
8: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 9
}, {
tooltip: "",
nextQuestion: 9
}, {
tooltip: "",
nextQuestion: 9
}]
},
9: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 10
}, {
tooltip: "",
nextQuestion: 10
}, {
tooltip: "",
nextQuestion: 10
}]
},
10: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 11
}, {
value: "Roof",
attribute: "Flue Exit",
tooltip: "",
nextQuestion: 15
}]
},
11: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 12
}, {
tooltip: "",
nextQuestion: 12
}]
},
12: {
finalQuestion: true,
input: true,
placeHolder: 'e.g SWS'
},
13: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 6
}, {
tooltip: "",
nextQuestion: 6
}]
},
14: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 10000
}]
},
15: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 12
}, {
tooltip: "",
nextQuestion: 12
}]
},
17: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 8
}, {
tooltip: "",
nextQuestion: 8
}, {
tooltip: "",
nextQuestion: 8
}, {
tooltip: "",
nextQuestion: 8
}, {
tooltip: "",
nextQuestion: 8
}]
},
// Errors
10000: {
isError: true,
title: "Finally, what is the first part of your postcode?",
error: "Postcode"
}
};
class QuestionAnswerPossibilities {
constructor(questions, ...path) {
// Data fields
this.questions = questions;
this.path = path;
// Derived fields
const currentStep = this.path[this.path.length - 1];
this.currentQuetion = this.questions[currentStep];
this.isFinished = this.currentQuetion.finalQuestion || this.currentQuetion.isError || false;
}
clone(andNextStep) {
return new QuestionAnswerPossibilities(this.questions, ...this.path, andNextStep);
}
possibleNext() {
const nextSteps = (this.currentQuetion.options ? this.currentQuetion.options : []).map(x => x.nextQuestion);
return new Set(nextSteps);
}
takeStep() {
if (this.isFinished) {
return [this];
}
return Array.from(this.possibleNext(), step => this.clone(step));
}
static start(questions) {
// Find any possible first questions
const first = Object.entries(questions).filter(([key, q]) => q.firstQuestion);
// For each first question create an agent
let pathExplorers = first.map(([start]) => new QuestionAnswerPossibilities(questions, start));
// Get all agents to continue until they are all finished
while (pathExplorers.some(x => !x.isFinished)) {
pathExplorers = pathExplorers.flatMap(x => x.takeStep());
}
return pathExplorers;
}
}
const MatchingAnswerPaths = (answerPaths, questionsAnswered) => {
let possiblePaths = [];
answerPaths.forEach(answerPath => {
let matchingPathFound = questionsAnswered.every((r, index) => {
return answerPath[index] === r;
});
if (matchingPathFound) {
possiblePaths.push(answerPath);
}
});
return possiblePaths;
}
const GetLongestAnswerPath = (possiblePaths) => {
let longest = 0;
let longestPath = [];
possiblePaths.forEach((path) => {
if (path.length > longest) {
longestPath = [path];
longest = path.length;
} else if (path.length == longest) {
longestPath.push(path);
}
});
if (longestPath.length > 0) {
return longestPath[0];
}
}
// Answered Paths
let answeredPaths = [1, 2, 3, 5];
// Get possibilities
let possibilities = QuestionAnswerPossibilities.start(originalQuestions);
possibilities = possibilities.map(agent => agent.path);
possibilities = possibilities.map(path => path.map(step => String(step)));
// Get Matched paths
let matchedPaths = MatchingAnswerPaths(possibilities, answeredPaths);
console.log(possibilities);
console.log(matchedPaths);
作品:
answeredPaths = [1, 2, 3, 4, 5];
失败:
answeredPaths = [1, 2, 3, 5];
它失败的原因是因为它根据正确的顺序找到了所有的可能性,它没有考虑到跳过的问题,所以从3 -> 5
开始,而不是从3 -> 4 -> 5
的标准顺序开始;
可能的解决方案:
当从3跳到5时,我强制将问题4添加到已回答的路径中。然而,我不想为此编写一些静态代码。我想要匹配的路径来解决这个问题。
在尝试了很多很多解决方案之后,我终于找到了一个真正有效的方法:
这也将涵盖多个跳过,例如
answeredPaths = [1, 2, 3, 5];
answeredPaths = [1, 2, 3, 6];
answeredPaths = [1, 3, 6];
原代码:
const MatchingAnswerPaths = (answerPaths, questionsAnswered) => {
let possiblePaths = [];
answerPaths.forEach(answerPath => {
let matchingPathFound = questionsAnswered.every((r, index) => {
return answerPath[index] === r;
});
if (matchingPathFound) {
possiblePaths.push(answerPath);
}
});
return possiblePaths;
}
固定代码:
const MatchingAnswerPaths = (answerPaths, questionsAnswered) => {
let possiblePaths = [];
answerPaths.forEach(answerPath => {
let matchingPathFound = questionsAnswered.every((r, index) => {
let answerPathIndex = answerPath.indexOf(r);
return answerPath[answerPathIndex] === r;
});
if (matchingPathFound) {
possiblePaths.push(answerPath);
}
});
return possiblePaths;
}
const originalQuestions = {
1: {
title: "Title",
firstQuestion: true,
options: [{
tooltip: "",
nextQuestion: 2
}, {
tooltip: "",
nextQuestion: 2
}, {
tooltip: "",
nextQuestion: 10000
}]
},
2: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 3
}, {
tooltip: "",
nextQuestion: 3
}, {
tooltip: "",
nextQuestion: 3
}, {
tooltip: "",
nextQuestion: 3
}]
},
3: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 4
}, {
tooltip: "",
nextQuestion: 4
}, {
tooltip: "",
nextQuestion: 4
}]
},
4: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 13
}, {
tooltip: "",
nextQuestion: 5
}]
},
5: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 6
}, {
tooltip: "",
nextQuestion: 6
}, {
tooltip: "",
nextQuestion: 6
}, {
tooltip: "",
nextQuestion: 10000
}]
},
6: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 14
}]
},
7: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 17
}, {
tooltip: "",
nextQuestion: 17
}, {
tooltip: "",
nextQuestion: 17
}, {
tooltip: "",
nextQuestion: 17
}, {
tooltip: "",
nextQuestion: 17
}]
},
8: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 9
}, {
tooltip: "",
nextQuestion: 9
}, {
tooltip: "",
nextQuestion: 9
}]
},
9: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 10
}, {
tooltip: "",
nextQuestion: 10
}, {
tooltip: "",
nextQuestion: 10
}]
},
10: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 11
}, {
value: "Roof",
attribute: "Flue Exit",
tooltip: "",
nextQuestion: 15
}]
},
11: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 12
}, {
tooltip: "",
nextQuestion: 12
}]
},
12: {
finalQuestion: true,
input: true,
placeHolder: 'e.g SWS'
},
13: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 6
}, {
tooltip: "",
nextQuestion: 6
}]
},
14: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 7
}, {
tooltip: "",
nextQuestion: 10000
}]
},
15: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 12
}, {
tooltip: "",
nextQuestion: 12
}]
},
17: {
title: "Title",
options: [{
tooltip: "",
nextQuestion: 8
}, {
tooltip: "",
nextQuestion: 8
}, {
tooltip: "",
nextQuestion: 8
}, {
tooltip: "",
nextQuestion: 8
}, {
tooltip: "",
nextQuestion: 8
}]
},
// Errors
10000: {
isError: true,
title: "Finally, what is the first part of your postcode?",
error: "Postcode"
}
};
class QuestionAnswerPossibilities {
constructor(questions, ...path) {
// Data fields
this.questions = questions;
this.path = path;
// Derived fields
const currentStep = this.path[this.path.length - 1];
this.currentQuetion = this.questions[currentStep];
this.isFinished = this.currentQuetion.finalQuestion || this.currentQuetion.isError || false;
}
clone(andNextStep) {
return new QuestionAnswerPossibilities(this.questions, ...this.path, andNextStep);
}
possibleNext() {
const nextSteps = (this.currentQuetion.options ? this.currentQuetion.options : []).map(x => x.nextQuestion);
return new Set(nextSteps);
}
takeStep() {
if (this.isFinished) {
return [this];
}
return Array.from(this.possibleNext(), step => this.clone(step));
}
static start(questions) {
// Find any possible first questions
const first = Object.entries(questions).filter(([key, q]) => q.firstQuestion);
// For each first question create an agent
let pathExplorers = first.map(([start]) => new QuestionAnswerPossibilities(questions, start));
// Get all agents to continue until they are all finished
while (pathExplorers.some(x => !x.isFinished)) {
pathExplorers = pathExplorers.flatMap(x => x.takeStep());
}
return pathExplorers;
}
}
const MatchingAnswerPaths = (answerPaths, questionsAnswered) => {
let possiblePaths = [];
answerPaths.forEach(answerPath => {
let matchingPathFound = questionsAnswered.every((r, index) => {
let answerPathIndex = answerPath.indexOf(r);
return answerPath[answerPathIndex] === r;
});
if (matchingPathFound) {
possiblePaths.push(answerPath);
}
});
return possiblePaths;
}
const GetLongestAnswerPath = (possiblePaths) => {
let longest = 0;
let longestPath = [];
possiblePaths.forEach((path) => {
if (path.length > longest) {
longestPath = [path];
longest = path.length;
} else if (path.length == longest) {
longestPath.push(path);
}
});
if (longestPath.length > 0) {
return longestPath[0];
}
}
// Answered Paths
let answeredPaths = ["1", "2", "3", "5", "6"];
// Get possibilities
let possibilities = QuestionAnswerPossibilities.start(originalQuestions);
possibilities = possibilities.map(agent => agent.path);
possibilities = possibilities.map(path => path.map(step => String(step)));
// Get Matched paths
let matchedPaths = MatchingAnswerPaths(possibilities, answeredPaths);
//console.log(possibilities);
console.log(matchedPaths);