Get Possible Question Answer Paths (JavaScript)
I'm looking to get all the possible question answer paths based on question options, I've been racking my brain all day and can't seem to figure out why my code isn't working. Test
Solution 1:
You can use a recursive generator fuction.
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 }] }, 10000: { isError: true, title: "Finally, what is the first part of your postcode?", error: "Postcode" } };
function* fnName(key, result = [key]) {
let options = originalQuestions[key]?.options;
if (!options) yield result
else for (const id of new Set(options.map(v => v.nextQuestion)))
yield* fnName(id, result.concat(id));
}
console.log("Prettify", Array.from(fnName(1), path => path.join(", ")))
I simple pass 1st question id (fnName(1)
), in this case its number 1
Because this is the 1st question. (originalQuestions[1].firstQuestion = true
)
Solution 2:
Send some agents that will explore your paths. Each agent keeps track of where it has been. It can also clone itself in order to explore on multiple paths:
class Agent {
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 Agent(this.questions, ...this.path, andNextStep);
}
possibleNext() {
const nextSteps = (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 Agent(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 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 }] }, 10000: { isError: true, title: "Finally, what is the first part of your postcode?", error: "Postcode" } };
const result = Agent.start(originalQuestions)
.map(agent => agent.path);
console.log(
//more concise printing
result.map(path => path
.map(step => String(step).padStart(2))
.join(" -> "))
);
Solution 3:
Nur's wonderful answer is almost exactly how I would solve this. I might suggest a few changes that makes this different enough to warrant a separate post -
function* paths(t, key) {
const { options } = t[key] ?? {}
if (options == null) return yield [key]
for (const id of new Set(options.map(v => v.nextQuestion)))
for (const path of paths(t, id))
yield [key, ...path]
}
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 }] }, 10000: { isError: true, title: "Finally, what is the first part of your postcode?", error: "Postcode" } }
for (const path of paths(originalQuestions, 1))
console.log(path.join(" -> "))
.as-console-wrapper { min-height: 100%; }
1 -> 2 -> 3 -> 4 -> 13 -> 6 -> 7 -> 17 -> 8 -> 9 -> 10 -> 11 -> 12
1 -> 2 -> 3 -> 4 -> 13 -> 6 -> 7 -> 17 -> 8 -> 9 -> 10 -> 15 -> 12
1 -> 2 -> 3 -> 4 -> 13 -> 6 -> 14 -> 7 -> 17 -> 8 -> 9 -> 10 -> 11 -> 12
1 -> 2 -> 3 -> 4 -> 13 -> 6 -> 14 -> 7 -> 17 -> 8 -> 9 -> 10 -> 15 -> 12
1 -> 2 -> 3 -> 4 -> 13 -> 6 -> 14 -> 10000
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 17 -> 8 -> 9 -> 10 -> 11 -> 12
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 17 -> 8 -> 9 -> 10 -> 15 -> 12
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 14 -> 7 -> 17 -> 8 -> 9 -> 10 -> 11 -> 12
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 14 -> 7 -> 17 -> 8 -> 9 -> 10 -> 15 -> 12
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 14 -> 10000
1 -> 2 -> 3 -> 4 -> 5 -> 10000
1 -> 10000
Post a Comment for "Get Possible Question Answer Paths (JavaScript)"