Related
Hello I have JSON structured like this,
and I need to iterate over the items
[
{
"name": "About You",
"questions": [
{
"questionText": "What is your surname?",
"answers": [
{
"text": "Thomas"
}
]
},
{
"questionText": "Where do you work?",
"answers": [
{
"text": "Finance"
}
]
},
]
},
{
"name": "About Family",
"questions": [
{
"questionText": "Childeren",
"answers": [
{
"text": "Yes"
}
]
},
{
"questionText": "Married",
"answers": [
{
"text": "No"
}
]
},
]
},
{
"name": "Travel",
"questions": [
{
"questionText": "Do you travel a lot?",
"answers": [
{
"text": "Yes"
}
]
}
]
}
]
I started with this code but I don't know how to show all the nested items. Should I use another map function? I would like to have format that will show table with questionTexts and answers text
{details.map((detail, i) => (
<div key={i}>
<div>{detail.name}</div>
<div>{detail.questions ? detail.questions[0].questionText : ''}</div>
<div>{detail.questions ? detail.questions[0].answers[0].text : ''}</div>
</div>
)
)}
thank you
Should I use another map function?
Yes
First, map through the details array
details.map((detail, i) =>
Map through the current detail.questions
detail.questions.map((question) => (
Optionally, apply the same logic for question.answers
question.answers.map((question) => (
Small example were we show all the questions:
class Example extends React.Component {
render() {
const details = [{"name": "About You", "questions": [{"questionText": "What is your surname?", "answers": [{"text": "Thomas"} ] }, {"questionText": "Where do you work?", "answers": [{"text": "Finance"} ] }, ] }, {"name": "About Family", "questions": [{"questionText": "Childeren", "answers": [{"text": "Yes"} ] }, {"questionText": "Married", "answers": [{"text": "No"} ] }, ] }, {"name": "Travel", "questions": [{"questionText": "Do you travel a lot?", "answers": [{"text": "Yes"} ] } ] } ];
return (
details.map((detail, i) => (
<div key={detail.name} class='question'>
<b>{detail.name}</b>
{
detail.questions.map((question) => (
<em key={question.questionText}>
Question: {question.questionText}
</em>
))
}
</div>
))
);
}
}
ReactDOM.render(<Example />, document.body);
.question {
display: flex;
flex-direction: column;
margin-bottom: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Additional notes
Changed the key={} to the name of the question to we're sure it's unique
Added a key to the nested map(), using the question itself
Thx to #pilchard for noticing
I have this json:
[
{
"question": "1.1",
"level": 1,
"id": 4,
"answers": [
{
"text_answer": "NO",
"questions": [
{
"question": "1.1.1",
"level": 2,
"id": 3,
"answers": []
}
]
},
{
"text_answer": null,
"questions": [
{
"question": "1.1.2",
"level": 2,
"id": 2,
"answers": [
{
"text_answer": "SI",
"questions": [
{
"question": "1.1.2.1",
"level": 3,
"id": 1,
"answers": []
}
]
}
]
}
]
}
]
}
]
this json is dynamic and can have n amount of levels under the fields answers and questions that is why it is necessary to go through them with a recursive function
I want to get this output:
[
{
"question": "1.1",
"level": 1,
"id": 4,
"children": [
{
"question": "1.1.1",
"text_answer": "NO",
"level": 2,
"id": 3,
"children": []
},
{
"question": "1.1.2",
"text_answer": null,
"level": 2,
"id": 2,
"children": [
{
"question": "1.1.2.1",
"text_answer": "SI",
"level": 3,
"id": 1,
"children": []
}
]
}
]
}
]
the fields answers and questions no longer exist, they are renamed children and the content of both is unified.
I think this recursive function should do a double foreach, but I don't know how to do it.
function format(d) {
if (d.respuestas) {
d.respuestas.forEach((d) => {
format;
});
}
}
format(data);
var data= [
{
"question": "1.1",
"level": 1,
"id": 4,
"answers": [
{
"text_answer": "NO",
"questions": [
{
"question": "1.1.1",
"level": 2,
"id": 3,
"answers": []
}
]
},
{
"text_answer": null,
"questions": [
{
"question": "1.1.2",
"level": 2,
"id": 2,
"answers": [
{
"text_answer": "SI",
"questions": [
{
"question": "1.1.2.1",
"level": 3,
"id": 1,
"answers": []
}
]
}
]
}
]
}
]
}
]
/* [
{
"question": "1.1",
"level": 1,
"id": 4,
"children": [
{
"question": "1.1.1",
"text_answer": "NO",
"level": 2,
"id": 3,
"children": []
},
{
"question": "1.1.2",
"text_answer": null,
"level": 2,
"id": 2,
"children": [
{
"question": "1.1.2.1",
"text_answer": "SI",
"level": 3,
"id": 1,
"children": []
}
]
}
]
}
]
*/
function format(d) {
if (d.answers) {
d.answers.forEach((d) => {
format;
});
}
}
format(data);
**note:**
I changed the previous structure that I put to the question to make myself understand better.
Here is one approach via recurssion. I've commented the code to explain what exactly is going on line by line.
function _process(item) {
let result = item // we define a copy of item -> result
// this is renaming the field 'answers' (if found)
if (item.answers) {
result.children = item.answers.map(_process) // process each item separately
delete result.answers
}
// if there is a question field, extract the first item in the array and merge the attributes with result
if (item.questions) {
result = {
...result,
..._process(item.questions[0]) // also try to process the item before merging it (to check if there are nested 'questions' or 'answers' fields)
}
delete result.questions // remove the questions field
}
return result
}
function process(data) {
return data.map(_process) // start the recurssion for top-level objects
}
// sample data to test out
const data = [{
"question": "1.1",
"level": 1,
"id": 4,
"answers": [{
"text_answer": "NO",
"questions": [{
"question": "1.1.1",
"level": 2,
"id": 3,
"answers": []
}]
},
{
"text_answer": null,
"questions": [{
"question": "1.1.2",
"level": 2,
"id": 2,
"answers": [{
"text_answer": "SI",
"questions": [{
"question": "1.1.2.1",
"level": 3,
"id": 1,
"answers": []
}]
}]
}]
}
]
}]
const result = process(data)
console.log(JSON.stringify(result, null, 2))
You're on the right track in writing a recursive function.
Where I think you're going wrong is in thinking about 'pushing' it to an array or object to store the data.
Instead, what should happen is that your recursive function returns the formatted data, and then that data is added to new json object which is sent back up the call stack.
var data = {
"name": "1.question",
"answer": "YES",
"children": [{
"name": "1.1 question",
"answer": "yes"
},
{
"name": "1.2 question",
"answer": "NO",
"children": [{
"name": "1.2.1 question",
"answer": "NO"
}]
},
{
"name": "1.3 question",
"answer": "YES",
"children": [{
"name": "1.3.1 question",
"answer": "yes",
"children": [{
"name": "1.3.1.1 question",
"answer": "YES"
}]
}]
}
]
}
function format(d) {
if (d.children) {
// if there are children you are going to recurse deeper
// use the Array.prototype.map function to _transform_ each of the children.
const formattedChildren = d.children.map(v => {
return format(v);
});
// Notice that I'm returning a new object here, as well as
// An aggregation of the already transformed data
return {
data: "new formatted parent node data goes here",
children: formattedChildren
};
} else { // _always_ have a check for 'is it a leaf node'.
// If it's a leaf node node, just format it.
//Notice that I'm returning a new object here
return {
data: "new formatted leaf node data goes here"
};
}
}
console.log(format(data));
I don't know what you are trying to achieve, so I've left this blank. But this is the template for how you would recursively traverse and transform a nested object like this.
(Note that you don't need to have a data key, I've just put that in as a place holder. It looks like you want name and answered keys.
It works for me:
// here is your JSON
var json = [{
"question": "1.1",
"level": 1,
"id": 4,
"answers": [{
"text_answer": "NO",
"questions": [{
"question": "1.1.1",
"level": 2,
"id": 3,
"answers": []
}]
},
{
"text_answer": null,
"questions": [{
"question": "1.1.2",
"level": 2,
"id": 2,
"answers": [{
"text_answer": "SI",
"questions": [{
"question": "1.1.2.1",
"level": 3,
"id": 1,
"answers": []
}]
}]
}]
}
]
}]
// main
function change(branch) {
// the first level has a bit different structure
try {
if (branch.level == 1) {
return {
question: branch.question,
level: branch.level,
id: branch.id,
children: change(branch.answers)
}
}
} catch (e) {}
// next levels
var new_branch = [];
for (var i = 0; i < branch.length; i++) {
new_branch.push({
question: branch[i].questions[0].question,
text_answer: branch[i].text_answer,
level: branch[i].questions[0].level,
id: branch[i].questions[0].id,
children: [change(branch[i].questions[0].answers)].flat(1)
});
}
return new_branch;
}
// output
console.log(JSON.stringify(change(json[0])));
Hi I am trying to build a question and answer based on conditional flows.
Let's take my question in UI is below JSON:
{
"text": "What is the best cookie?",
"type": "mc",
"answers": [
"Chocolate Chip",
"Sugar",
"Beer"
],
"answer": "Sugar"
}
Let's take if the user selects Sugar and Beer as an option, I have put up a question from below JSON which maps to the selected answer if he chooses Beer then a different question from the JSON object.
If the user answers A and B for the first question, then the next question should be 3rd object in JSON, if C then 2nd Object from JSON, if C and A then the first Object from JSON.
Below is entire mock JSON I have
{
"title": "Quiz about Foo",
"questions": [
{
"text": "Is true true?",
"type": "tf",
"answer": "t"
},
{
"text": "Is false true?",
"type": "tf",
"answer": "f"
},
{
"text": "What is the best beer?",
"type": "mc",
"answers": [
"Coors",
"Miller",
"Bud",
"Anchor Steam"
],
"answer": "Anchor Steam"
},
{
"text": "What is the best cookie?",
"type": "mc",
"answers": [
"Chocolate Chip",
"Sugar",
"Beer"
],
"answer": "Sugar"
}
]
}
I'm not sure I fully understand your question. If you want each answer to point to the next question in your JSON, you have to add some kind of a question ID. You also need to convert your answers from strings to objects, so you can add the ID of the next question to them.
Something like:
{
"questions": [
{
"id": 1,
"text": "What is the best cookie?",
"type": "mc",
"answers": [
{ "text": "Chocolate Chip", "nextQuestionId": 2 },
{ "text": "Sugar", "nextQuestionId": 2 },
{ "text": "Beer", "nextQuestionId": 3 }
],
"answer": "Sugar"
},
{
"id": 2,
"text": "Second question?",
...
},
{
"id": 3,
"text": "Third question?",
...
}
]
}
This way you can write a simple generic logic that just follows the flow defined by the JSON.
Hi I have been trying make this following array
[
{
"name": "Study",
"questions": [
{
"question": "Would you Love to learn about Java?",
"answer": "Yes"
}
]
},
{
"name": "Song",
"questions": [
{
"question": "Would you Love to learn about song?",
"answer": "Yes"
},
{
"question": "Would you Love to learn about rock?",
"answer": "No"
}
]
}
]
To this
[
{
"questions": [
{
"question": "Would you Love to learn about Java?",
"answer": "Yes",
"name": "Study"
}
]
},
{
"questions": [
{
"question": "Would you Love to learn about song?",
"answer": "Yes",
"name": "Song"
},
{
"question": "Would you Love to learn about rock?",
"answer": "No",
"name": "Song"
}
]
}
]
I have tried to map to two arrays ( from name and questions) and tried to combined them , but I can't make it work for multiple arrays in questions. How can I make this ? Is there any loadash way too accomplish this easily. But I'm trying to do this in Vanilla JavaScript way.
You can try with double .map():
let input = [
{
"name": "Study",
"questions": [
{
"question": "Would you Love to learn about Java?",
"answer": "Yes"
}
]
},
{
"name": "Song",
"questions": [
{
"question": "Would you Love to learn about song?",
"answer": "Yes"
},
{
"question": "Would you Love to learn about rock?",
"answer": "No"
}
]
}
]
let result = input.map(({name, questions}) => ({ questions: questions.map(q => ({name, ...q })) }));
console.log(result);
Try following
let arr1 = [
{
"name": "Study",
"questions": [
{
"question": "Would you Love to learn about Java?",
"answer": "Yes"
}
]
},
{
"name": "Song",
"questions": [
{
"question": "Would you Love to learn about song?",
"answer": "Yes"
},
{
"question": "Would you Love to learn about rock?",
"answer": "No"
}
]
}
]
arr1.map((element, index) => {
console.log(element); console.log(index); return
{
questions: element.questions.map((e, i) => {
console.log(e); console.log(i); return {
...e,
name: element.name
};
})
}
})
In my application I have a ajax call and am getting the following response based on this am constructing the questions & answer section which I have included in the JSFiddle
var responseQuestions = {
"error": false,
"message": "Success",
"result": {
"Questions": [{
"Id": "131a",
"Text": "In what county do you live?",
"Answers": [{
"Id": "abc1",
"Text": "option1"
},
{
"Id": "abc2",
"Text": "option2"
},
{
"Id": "abc3",
"Text": "option3"
},
{
"Id": "abc4",
"Text": "option4"
},
{
"Id": "abc5",
"Text": "option5"
}
],
"SelectedAnswerId": null
},
{
"Id": "132a",
"Text": "Which zip code has ever been a part of your address?",
"Answers": [{
"Id": "def1",
"Text": "option1"
},
{
"Id": "def2",
"Text": "option2"
},
{
"Id": "def3",
"Text": "option3"
},
{
"Id": "def4",
"Text": "option4"
},
{
"Id": "def5",
"Text": "option5"
}
],
"SelectedAnswerId": null
},
{
"Id": "133a",
"Text": "What was the original amount of your most recent mortgage?",
"Answers": [{
"Id": "ghi1",
"Text": "option1"
},
{
"Id": "ghi2",
"Text": "option2"
},
{
"Id": "ghi3",
"Text": "option3"
},
{
"Id": "ghi4",
"Text": "option4"
},
{
"Id": "ghi5",
"Text": "option5"
}
],
"SelectedAnswerId": null
}
]
}
};
Required Format:
var responseQuestions = {
"error": false,
"message": "Success",
"result": {
"Questions": [{
"Id": "131a",
"Text": "In what county do you live?",
"Answers": [{
"Id": "abc1",
"Text": "option1"
},
{
"Id": "abc2",
"Text": "option2"
},
{
"Id": "abc3",
"Text": "option3"
},
{
"Id": "abc4",
"Text": "option4"
},
{
"Id": "abc5",
"Text": "option5"
}
],
**"SelectedAnswerId": "abc2"**
},
{
"Id": "132a",
"Text": "Which zip code has ever been a part of your address?",
"Answers": [{
"Id": "def1",
"Text": "option1"
},
{
"Id": "def2",
"Text": "option2"
},
{
"Id": "def3",
"Text": "option3"
},
{
"Id": "def4",
"Text": "option4"
},
{
"Id": "def5",
"Text": "option5"
}
],
**"SelectedAnswerId": "def1"**
},
{
"Id": "133a",
"Text": "What was the original amount of your most recent mortgage?",
"Answers": [{
"Id": "ghi1",
"Text": "option1"
},
{
"Id": "ghi2",
"Text": "option2"
},
{
"Id": "ghi3",
"Text": "option3"
},
{
"Id": "ghi4",
"Text": "option4"
},
{
"Id": "ghi5",
"Text": "option5"
}
],
**"SelectedAnswerId": "ghi2"**
}
]
}
};
Now I need to submit this answer with the same above mentioned format along the "SelectedAnswerId" value(in the above mentioned array "SelectedAnswerId" is null and now I have to include the original selected ans Id based on questions ).
I have tried to fetch all the selected ans id in a array and attached the same in jsfiddle but am unable to proceed on how to append this ans id in the existing array based on questions. How to achieve this ?
Try the following loops:
$('input[type="radio"]:checked').each(function() {
var questionId = $(this).closest('.radioGroup').prev().attr('id');//get the question id
var answerId = this.id;//get the answer id
$.each(responseQuestions.result.Questions, function(i, v) {//loop each question
if (v.Id == questionId) {
$.each(v.Answers, function(ind, val) {//loop each answer
if (val.Id == answerId) {
responseQuestions.result.Questions[i]['SelectedAnswerId'] = answerId;//save the answer
}
});
}
});
});
console.log(responseQuestions);
demo:https://jsfiddle.net/mj3gvd5e/1/
Note: you need to change your question id to remove or add the a to be consistent both in the page and in the json
You have to find index based on question id and then set answerid at specific index
var index = responseQuestions.result.Questions.findIndex(function(val){
return val.Id === yourQuestionid;
})
responseQuestions.result.Questions[index].SelectedAnswerId = youranswerID;
First of all you need to have your main question id and Ans id into same array as given fiddle by Madalin jsfiddle.net/6q9mct68
second you get this array, you can loop through your first array and replace the string by compairing Id,