yavg
yavg

Reputation: 3051

How can I add the result of a recursive function in a json?

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.

Upvotes: 2

Views: 233

Answers (3)

Yuri Khristich
Yuri Khristich

Reputation: 14537

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])));

Upvotes: 0

Tibebes. M
Tibebes. M

Reputation: 7548

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))

Upvotes: 6

dwjohnston
dwjohnston

Reputation: 11812

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.

Upvotes: 1

Related Questions