Получите возможные пути ответов на вопросы (JavaScript)

#javascript #arrays

Вопрос:

Я ищу все возможные пути ответов на вопросы, основанные на вариантах вопросов, я весь день ломал голову и, похоже, не могу понять, почему мой код не работает.

Тестовый код:

 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"
    }
};

function loopPaths(currentArrayPath, questionNum, currentQuestion, paths) {

    if (questionNum > 5) {
   return false;
  }

    if (typeof currentQuestion.finalQuestion != 'undefined') {
    return false;
  } else {
    const question = currentQuestion.options;  
    const validPaths = question.filter((e) => e.nextQuestion !== 10000);   
    const clonedPath = [...paths[currentArrayPath][0]];
            
    for (var i = 0; i < validPaths.length; i  ) {
      const e = validPaths[i];
            
      if (typeof paths[currentArrayPath][i] == 'undefined') {
        paths[currentArrayPath][i] = [...clonedPath];
      }
      
      paths[currentArrayPath][i].push(questionNum);
      
      loopPaths(currentArrayPath, e.nextQuestion, originalQuestions[e.nextQuestion], paths);
    }
  }
}

function possiblePaths() {
    const question1 = originalQuestions[1].options;
    const validPaths = question1.filter((e) => e.nextQuestion !== 10000);
  
  let paths = [];
  
  /*validPaths.forEach((e, i) => {
    paths[i] = [[1]];
  });*/
      
  /* 
  for (var i = 0; i < validPaths.length; i  ) {
    const e = validPaths[i];
           
    loopPaths(e.nextQuestion, boilerQuestions[e.nextQuestion], paths);
  }   */
  
  // Testing with first question, first option
  paths[0] = [[1]];
  loopPaths(0, 2, originalQuestions[2], paths);
  
  console.log(paths);
}

possiblePaths(); 

Что, кажется, происходит: (Это не построение правильных путей и не построение различных возможностей)

 [
  [
    [
      1,
      2,
      3,
      4,
      5,
      4,
      5,
      4,
      5,
      3,
      4,
      5,
      4,
      5,
      4,
      5,
      3,
      4,
      5,
      4,
      5,
      4,
      5,
      3,
      4,
      5,
      4,
      5,
      4,
      5
    ],
    [
      1,
      2,
      3,
      4,
      5,
      3,
      4,
      5,
      4,
      5,
      2,
      4,
      5,
      3,
      4,
      5,
      4,
      5,
      4,
      5,
      3,
      4,
      5,
      4,
      5,
      4,
      5,
      3,
      4,
      5,
      4,
      5
    ]
  ]
]
 

Пример того, что он должен показать: (Мне интересно, есть ли более простой способ построить различные возможности от 1 -> до последнего вопроса, который равен 12)

 [
  [
    [
      1,
      2,
      3,
      4,
      13,
      7,
      17,
      8,
      9,
      10,
      11,
      12
    ],
    [
      1,
      2,
      3,
      4,
      5,
      6,
      7,
      ....
    ]
  ]
]
 

Как это будет работать:

Пользователь нажимает вопрос 1 -> Нажимает вариант 1 ->> Пользователь нажимает вопрос 2 ->>> Нажимает вариант 2 ->>>> Пользователь нажимает вопрос 3 ->>>>> Нажимает вариант 1 — > > > > > > > Пользователь нажимает вопрос 4 — > > > > > > > > Нажимает вариант 1 ->>>>>>>> Пользователь задает вопрос 13

Ответ №1:

Вы можете использовать функцию рекурсивного генератора.

 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(", "))) 

Я просто передаю идентификатор 1-го вопроса ( fnName(1) ), в данном случае его номер 1 , потому что это 1-й вопрос. ( originalQuestions[1].firstQuestion = true )

Ответ №2:

Пошлите несколько агентов, которые изучат ваши пути. Каждый агент отслеживает, где он был. Он также может клонировать себя, чтобы исследовать по нескольким путям:

 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(" -> "))
); 

Ответ №3:

Замечательный ответ Нура-это почти точно то, как бы я решил эту проблему. Я мог бы предложить несколько изменений, которые делают это достаточно другим, чтобы оправдать отдельный пост —

 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