Arrange the elements in the array based on their parent relationships (using topological sort) and their importance within

My array contains objects with elements that are dependent on each other.

To properly store this data in a database, I need to order the array by importance (based on parent dependency) and update all children's parent property with the corresponding parent id.

Here is an example of the array structure:

[
    {
        "id": 1,
        "email": "example1@example.com", 
        "parent": "parent1@example.com"
    },
    {
        "id": 2,
        "email": "example2@example.com",
        "parent": null
    },
    {
        "id": 3,
        "email": "example3@example.com",
        "parent": "parent2@example.com"
    },
    {
        "id": 4,
        "email": "example4@example.com",
        "parent": "parent3@example.com"
    },
    ...
]

Below is a graphical representation of the dependencies:

https://i.sstatic.net/02P88.png

Expected result after ordering by dependency (parent level):

[
    {
        "id": 2,
        "email": "example2@example.com",
        "parent": null
    },
    {
        "id": 3,
        "email": "example3@example.com",
        "parent": 2
    },
    {
        "id": 1,
        "email": "example1@example.com",
        "parent": 3 
    },
    {
        "id": 4,
        "email": "example4@example.com",
        "parent": 1
    },
    ...
]

To assign respective parent id, I am using the following method:

let users = [
{
    "id": 1,
    "email": "user1@example.com",
    "parent": "parent1@example.com"
},
{
    "id": 2,
    "email": "user2@example.com",
    "parent": null
},
{
    "id": 3,
    "email": "user3@example.com",
    "parent": "parent2@example.com"
},
{
    "id": 4,
    "email": "user4@example.com",
    "parent": "parent3@example.com"
}
];

users = users.map(user => {
    user.parent = _.findIndex(users, i => user.parent === i.email);
    return user;
});

P.S: In this scenario, the term importance signifies the hierarchy of parent. Therefore, it is essential to first list the parents followed by their children, grandchildren, etc.

I apologize if my explanation is lacking clarity. Feel free to ask any questions for further clarification.

Answer №1

My approach will involve creating a new input by replacing the parent email with the parent id, and assigning a new property to each node indicating their level in the tree hierarchy. Then, I will sort the nodes based on this level property, and if two nodes have the same level, they will be sorted by their id.

const input = [
    {"id": 1, "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="97f6d7f5b9f4f8fa">[email protected]</a>", "parent": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d2b192b0fcb1bdbf">[email protected]</a>"},
    {"id": 2, "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8fedcfeda1ece0e2">[email protected]</a>", "parent": null},
    // More input objects here
];

// Additional JavaScript code for processing and sorting the input

console.log(sortedInput);

Answer №2

To solve this problem, you can implement a recursive function

const data = [{
    "id": 1,
    "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b7d6f7d599d4d8da">[email protected]</a>", // unique
    "parent": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c3a083a1eda0acae">[email protected]</a>" // is nullable
  },
  {
    "id": 2,
    "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c9ab89abe7aaa6a4">[email protected]</a>",
    "parent": null
  },
  {
    "id": 3,
    "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e88ba88ac68b8785">[email protected]</a>",
    "parent": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="325072501c515d5f">[email protected]</a>"
  },
  {
    "id": 4,
    "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d1b591b3ffb2bebc">[email protected]</a>",
    "parent": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b2d3f2d09cd1dddf">[email protected]</a>"
  },

]

const order = (arr, level) => {
  const children = arr.filter(e => e.parent === level); // select elements with the same parent as the specified level
  const parent = arr.find(e => e.email === level); // identify the parent
  
  return children.length 
    ? [
        ...children.map(e => 
          ({ ...e,
            parent: parent ? parent.id : null // update parent to ID instead of email
          })),
        ...order(arr, children[0].email) // recursively call the function with the first child's email as the new parent
      ] 
    : children // return the array if no children are found
}

const result = order(data, null)

console.log(result)

Answer №3

Here is a step-by-step iterative approach for achieving your desired outcome, focusing on finding the root element and then iterating through the array to identify elements with the current element as their parent.

To replace parent email with ID, you can maintain a map of parent names to IDs:

var data = [{
  "id": 1,
  "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5b3a1b3975383436">[email protected]</a>", // unique
  "parent": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="96f5d6f4b8f5f9fb">[email protected]</a>" // nullable
}, {
  "id": 2,
  "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="701230125e131f1d">[email protected]</a>",
  "parent": null
}, {
  "id": 3,
  "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="751635175b161a18">[email protected]</a>",
  "parent": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="84e6c4e6aae7ebe9">[email protected]</a>"
}, {
  "id": 4,
  "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ceaa8eace0ada1a3">[email protected]</a>",
  "parent": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5938193b773a3634">[email protected]</a>"
}]

//Map emails to IDs
var map = data.reduce((accum, el) => {
  accum[el.email] = {
    id: el.id
  }
  return accum;
}, {});


var [root] = data.filter(el => !el.parent);
var users = [root];
var cur;
var children;
while (users.length < data.length) {
  cur = users[users.length - 1];
  //Find elements with cur as parent
  children = data.filter(el => el.parent === cur.email);
  children.forEach(el => {
    users.push({
      id: el.id,
      email: el.email,
      parent: map[el.parent].id
    });
  });
}

console.log(users)

Answer №4

Although the provided answers are acceptable, they suffer from slow time complexity (O(n^2)). This is because they iterate over all nodes and for each node, they search for its parent which results in O(n) * O(n) = O(n^2) complexity.

An improved approach involves creating a tree structure and utilizing pre-order (DFS) for generating a topological sort.

function createTree(nodesWithParentArray) {
    const initialTree = nodesWithParentArray.reduce(
      (acc, node) => {
        acc[node.id] = { data: node, children: [] }

        return acc
      },
      { null: { children: [] } }
    )

    const tree = nodesWithParentArray.reduce((acc, node) => {
      acc[node.parent].children.push(acc[node.id])

      return acc
    }, initialTree)

    return tree[null].children[0] // root
}

// test it like that:
createTree([{id:1, parent:2},{id:2, parent:null},{id:3, parent:2},{id:4, parent:3}])

The function above will provide a nested tree structure with a reference to the root node. The next step is to utilize pre-order traversal for achieving a topological sort (O(n), as each node is traversed only once):

function topologicalSort(tree) {
    const sortedList = []
    const queue = [treeRoot]

    while (queue.length) {
      const curNode = queue.shift()
      sortedList.push(curNode.data)
      queue.push(...curNode.children)
    }

    return sortedList
}

Creating the tree mentioned earlier also has an O(n) complexity since it iterates through the input array just once. Hence, the final time complexity becomes O(n) + O(n) => O(n).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ways to extract Document ID from a Firestore database collection

Currently, I am in the process of developing a mobile app using React Native and Firebase. My main focus right now is on accessing document data without explicitly specifying the ID, unlike the method shown below: const docRef = db.collection('vehicle ...

Incorporate parent state changes in React Router using the Route component

I have a unique layout on my page consisting of a step progress bar at the top (similar to this example). In the center of the page, there is content that changes depending on which step is currently active. To handle the steps, I utilize a fixed component ...

Using an array in Android to add a date

I have a list filled with the "ID" values from a database table. To achieve my goal, I need to set specific dates on a calendar based on each ID position. For example, at position 0, I want to set the date as today's date; at position 1, the date shou ...

Error: SyntaxError - Found an unexpected token ":"

Can someone help me with this unexpected token : error that I am encountering? Here is the code where the issue is happening. <script type="text/javascript> $('.delete-btn').click(function() { $.ajax(function() { type: 'POST&apo ...

Update the displayed number on the input field as a German-formatted value whenever there is a change in the input, all while maintaining

In German decimal numbers, a decimal comma is used. When formatting, periods are also used for grouping digits. For example, the number 10000.45 would be formatted as 10.000,45. I am looking to create an input field (numeric or text) where users can input ...

Tips for making a 2D grid on a webpage

Is there a way to implement a 10x10 grid on a webpage where users can click anywhere on the grid and have their (x, y) position recorded with one decimal place accuracy, covering values between 0.0-10.0 or 0.1-9.9? Appreciate any guidance! ...

Ways to include multiple pieces of data in a JQuery Mobile List view

Obtaining JSON data (list of available Hotels within a certain distance) and extracting the following information in JSON format: Name of Hotels, Distance of Hotel from our current location, number of rooms. There might be multiple Hotels within the specif ...

Identifying the device name in Safari on iOS 13 despite the inaccurate display of the user agent - a step-by-step guide

Following the release of Apple's iOS 13, I discovered that window.navigator.userAgent in Safari on iPad iOS 13 is identical to that on MacOS. It appears like this: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) ...

Allow removal of input masks upon successful submission

At the moment, I am utilizing this script to remove my masks upon submission: $(".active_validator").submit(function() { $('.money_mask').unmask(); $('.num_mask_thou').unmask(); $('.zipcode').unmask(); $(&apos ...

Pause until the existence of document.body is confirmed

Recently, I developed a Chrome extension that runs before the page fully loads by setting the attribute "run_at": "document_start". One issue I encountered is that I need to insert a div tag into the body of the webpage as soon as it is created. However, a ...

Error: Trying to destructure a non-iterable object with useContext in React is not valid

ERROR [TypeError: Invalid attempt to destructure non-iterable instance. In order to be iterable, non-array objects must have a Symbol.iterator method.] Using UserContext : import React, { useContext, useEffect, useLayoutEffect, useState } from "reac ...

Is object position cloning performed by the three.js renderer?

I recently designed a small scene featuring 3 spheres connected by a triangle with vertices at the centers of the spheres. Surprisingly, when I moved one of the spheres, the triangle did not move with it as expected. It appears that the renderer may not be ...

Implement a CSS style for all upcoming elements

I'm currently developing a Chrome extension tailored for my personal needs. I am looking to implement a CSS rule that will be applied to all elements within a certain class, even if they are dynamically generated after the execution of my extension&ap ...

How can I transform an HTML element into a textarea using CSS or JavaScript?

While browsing a webpage, I discovered a <div> element with the class .plainMail and I want to find a way to easily select all its text by simply pressing Ctrl+A. Currently using Firefox 22, I am considering converting the div.plainMail into a texta ...

Restrict the amount of items fetched when processing JSON information

Here is a snippet of JSON data that I am parsing using JavaScript. "credits":{ "cast":[{ "id":819,"name":"Edward Norton","character":"The Narrator","order":0,"cast_id":4,"profile_path":"/iUiePUAQKN4GY6jorH9m23cbVli.jpg" }, {"id":287,"name": ...

Got any ideas for Laravel 8's One to One messaging feature?

I am looking to create a real-time one-to-one messaging system for the users of my application. I anticipate around 10,000 users. Instead of utilizing web sockets or similar solutions, I am currently using Livewire for other features. My initial thought wa ...

Steps to transfer extra files such as config/config.yaml to the .next directory

I have the following folder structure for my NextJS project: _posts/ components/ hooks/ config/ <--- includes config.yaml file for the server pages/ public/ .env.local ... yarn build successfully copies all dependencies except for the config/ folder. ...

Preventing browser freeze by using window.addEventListener in React

I'm new to React and I'm facing an issue where the code seems to freeze the browser (I'm using Chrome) when I click multiple times on the window. Can someone help me understand why? import "./App.css"; import { useState } from &quo ...

Utilizing an AngularJS service to communicate with a web API

Having trouble retrieving data from a web api and passing it back to a JavaScript file. I've tried using http://localhost:8584/api/Testing/5 to see if there are any results, but no luck so far. //This is the JavaScript controller that calls the serv ...

What is the process for including parameters in a javascript/jquery function?

This unique script enables you to click on a specific element without any consequences, but as soon as you click anywhere else, it triggers a fade-out effect on something else. Within the following code snippet, you can interact with elements inside $(&apo ...