Home > database >  Asynchronously POSTing parent-children objects and relationships (recursively?)
Asynchronously POSTing parent-children objects and relationships (recursively?)

Time:10-13

Say I am trying to create a "family tree" as one JSON array.

So I have an array of Person objects.

Each Person object has a mandatory name. Each object can also optionally have a children field that contains an array of other Person objects (that also have a children field - so the "depth" of the family tree can essentially go on forever.)

If there is no children, the children field will just be an empty array [].

E.g.

  const family_name = "The Numbers";
  const family = [{
      name: "1"
      children: [],
    },
    {
      name: "2"
      children: [{
          name: "2 - 1",
          children: [],
        },
        {
          name: "2 - 2",
          children: [{
            name: "2 - 2 - 1",
            children: [],
          }, ],
        }
      ],
    },
    {
      name: "3"
      children: [{
        name: "3 - 1",
        children: [],
      }, ],
    },
  ]

I need to POST the "parent" before the "child". When I POST a Person, I get its id back in the response.data. This id needs to be used in the immediate child's POST as a parent_id so that child will be associated to the parent.

I also need to POST the "family_name" first which will return a family_id. This family_id will be used as the parent_id for the very topmost Persons only.

e.g.

axios.post(FAMILY_URL, {"family_name": family_name})
    .then((response) => {
        // Promise.all() POST 1, 2, and 3 with their "parent_id" as response.data.family_id
        // Promise.all() POST 2-1 and 2-2 with their "parent_id" as 2's response.data.id
        // POST 2-2-1 with its "parent_id" as 2-1's response.data.id
        // POST 3-1 with its "parent_id" as 3's response.data.id
    })

But what does the code look like if the number and depth of Person objects is unknown? I have to leverage a recursive function, right?

I would also like to use Promise.all() for all "siblings"

CodePudding user response:

I would approach it using the BFS algorithm. Example:

function postFamily(family) {

    const queue = [];
    queue.unshift(...family);

    while (queue.length) {
        const cur = queue.pop();

        // HERE IS YOUR LOGIC RELATED TO THE CURRENT NODE
        // FOR EXAMPLE:
        axios.post('*FAMILY URL*', cur);

        for (const child of cur) {
            queue.unshift(child);
        }
    }
}

I don't clearly understand the wanted result, but i'm glad if it would help. Have a nice day!

CodePudding user response:

You want to recursively walk the data structure, accumulating promises along the way

// fake axios, ignore this bit
const FAMILY_URL = "family-url"
const axios = {
  post: async (url, data) => (console.log("post", data), {
    data: {
      id: `${url === FAMILY_URL ? "" : `${data.parent_id}-`}${Math.ceil(Math.random() * 100)}`
    }
  })
}

// resolves an individual person by posting their details with a given
// parent ID, then doing the same for any children
const savePerson = async (parent_id, { name, children }) => {
  // get this person's ID
  const { data: { id } } = await axios.post("person-url", {
    name,
    parent_id
  })
  
  return {
    id, // assign the ID for visibility
    name,
    // pass this person's ID as the children's parent
    children: await savePeople(id, children)
  }
}

// resolves an array of people given a "parent ID"
const savePeople = (parent_id, people) =>
  Promise.all(people.map(person => savePerson(parent_id, person)))

// Top-level family helper
const saveFamily = async (family_name, family) => {
  const { data: { id } } = await axios.post(FAMILY_URL, { family_name })
  
  // resolve all the people in the family array
  return savePeople(id, family)
}

// minified
const family = [{"name":"1","children":[]},{"name":"2","children":[{"name":"2 - 1","children":[]},{"name":"2 - 2","children":[{"name":"2 - 2 - 1","children":[]}]}]},{"name":"3","children":[{"name":"3 - 1","children":[]}]}]

saveFamily("Smith", family).then(console.info)
.as-console-wrapper { max-height: 100% !important; }

  • Related