JavaScript code to sort an array of objects

Hey everyone, I'm still having trouble sorting out this array properly. The array we need to sort is as follows:

const arr = [
     {id: 3123124, parentId: 0o0000, title: 'title', type: 'block'},
     {id: 3542132, parentId: 3123124, title: 'title', type: 'child'},
     {id: 12345, parentId: 88888, title: 'title', type: 'block'},
     {id: 24124, parentId: 12345, title: 'title', type: 'child'},
     {id: 99999, parentId: 45324, title: 'title', type: 'child'},
     {id: 986648, parentId: 3123124, title: 'title', type: 'block'},
     {id: 77777, parentId: 88888, title: 'title', type: 'child'},
     {id: 54232, parentId: 3123124, title: 'title', type: 'child'},
     {id: 54308, parentId: 15075, title: 'title', type: 'child'},
     {id: 66666, parentId: 88888, title: 'title', type: 'block'},
     {id: 56445, parentId: 12345, title: 'title', type: 'child'},
     {id: 88888, parentId: 45324, title: 'title', type: 'block'},
     {id: 15075, parentId: 12345, title: 'title', type: 'block'},
     {id: 84356, parentId: 66666, title: 'title', type: 'child'},
     {id: 45324, parentId: 0o0000, title: 'title', type: 'block'},
]

const newArr = [
    {id: 3123124, parentId: 0o0000, title: 'title', type: 'block'},
    {id: 3542132, parentId: 3123124, title: 'title', type: 'child'},
    {id: 54232, parentId: 3123124, title: 'title', type: 'child'},
    {id: 986648, parentId: 3123124, title: 'title', type: 'block'},
    {id: 45324, parentId: 0o0000, title: 'title', type: 'block'},
    {id: 99999, parentId: 45324, title: 'title', type: 'child'},
    {id: 88888, parentId: 45324, title: 'title', type: 'block'},
    {id: 77777, parentId: 88888, title: 'title', type: 'child'},
    {id: 12345, parentId: 88888, title: 'title', type: 'block'},
    {id: 56445, parentId: 12345, title: 'title', type: 'child'},
    {id: 24124, parentId: 12345, title: 'title', type: 'child'},
    {id: 15075, parentId: 12345, title: 'title', type: 'block'},
    {id: 54308, parentId: 15075, title: 'title', type: 'child'},
    {id: 66666, parentId: 88888, title: 'title', type: 'block'},
    {id: 84356, parentId: 66666, title: 'title', type: 'child'},
]
  • arr - the array that needs to be sorted
  • newArr - the desired output array after sorting

We want the elements with parentId: 0o000 at the top, followed by their children with matching parentId, and so on. If multiple elements have the same parentId, children should come first, then the most recent block and its children below it.

I attempted to add elements to a new array based on the parentId property, but ended up losing the original order of the elements.

Answer №1

To organize the items based on their parents and arrangement within each parent block, create objects to serve as lookup tables for IDs and parent grouping. Sort the items by parent first, then by block type.

const
    getItems = parent => (parents[parent] || [])
        .sort((a, b) => (ids[a].type === 'block') - (ids[b].type === 'block'))
        .flatMap(id => [ids[id], ...getItems(id)]),
    data = [{ id: 3123124, parentId: 0o0000, title: 'title', type: 'block' }, { id: 3542132, parentId: 3123124, title: 'title', type: 'child' }, { id: 12345, parentId: 88888, title: 'title', type: 'block' }, { id: 24124, parentId: 12345, title: 'title', type: 'child' }, { id: 99999, parentId: 45324, title: 'title', type: 'child' }, { id: 986648, parentId: 3123124, title: 'title', type: 'block' }, { id: 77777, parentId: 88888, title: 'title', type: 'child' }, { id: 54232, parentId: 3123124, title: 'title', type: 'child' }, { id: 54308, parentId: 15075, title: 'title', type: 'child' }, { id: 66666, parentId: 88888, title: 'title', type: 'block' }, { id: 56445, parentId: 12345, title: 'title', type: 'child' }, { id: 88888, parentId: 45324, title: 'title', type: 'block' }, { id: 15075, parentId: 12345, title: 'title', type: 'block' }, { id: 84356, parentId: 66666, title: 'title', type: 'child' }, { id: 45324, parentId: 0o0000, title: 'title', type: 'block' }],
    parents = data.reduce((r, { id, parentId }) => ((r[parentId] ??= []).push(id), r), {}),
    ids = Object.fromEntries(data.map(o => [o.id, o])),
    sorted = getItems(0);

console.log(sorted);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Answer №2

To create an ordered list of items, you can implement a recursive sortItems function. This function looks for items in the array with a specific identifier (either parentId or id) that matches the argument passed to the function. If a parent item is found, it is added to the sortedArr. Then, the child items within that parent are sorted. To improve clarity and organization, sortItems makes use of two helper functions: getParent and getChildren.

const arr = [
    { id: 3123124, parentId: 0o0000, title: 'title', type: 'block' },
    { id: 3542132, parentId: 3123124, title: 'title', type: 'child' },
    { id: 12345, parentId: 88888, title: 'title', type: 'block' },
    { id: 24124, parentId: 12345, title: 'title', type: 'child' },
    { id: 99999, parentId: 45324, title: 'title', type: 'child' },
    { id: 986648, parentId: 3123124, title: 'title', type: 'block' },
    { id: 77777, parentId: 88888, title: 'title', type: 'child' },
    { id: 54232, parentId: 3123124, title: 'title', type: 'child' },
    { id: 54308, parentId: 15075, title: 'title', type: 'child' },
    { id: 66666, parentId: 88888, title: 'title', type: 'block' },
    { id: 56445, parentId: 12345, title: 'title', type: 'child' },
    { id: 88888, parentId: 45324, title: 'title', type: 'block' },
    { id: 15075, parentId: 12345, title: 'title', type: 'block' },
    { id: 84356, parentId: 66666, title: 'title', type: 'child' },
    { id: 45324, parentId: 0o0000, title: 'title', type: 'block' },
];

const sortedArr = [];

const getParent = (parentId) => arr.find((item) => item.id === parentId);
const getChildren = (parentId) => arr.filter((item) => item.parentId === parentId);

const sortItems = (parentId = 0) => {
    const parent = getParent(parentId);
    if (parent) sortedArr.push(parent);
    const children = getChildren(parentId);
    children.sort((a, b) => (a.type === 'child' ? 0 : 1) - (b.type === 'child' ? 0 : 1));
    children.forEach((child) => sortItems(child.id));

};

sortItems();


console.log(sortedArr);

Answer №3

To create a hierarchical tree structure, one approach is to use a Map in JavaScript to map parent nodes to their children. Once you have this tree structure established, the process involves printing the key followed by its children for each node. To exclude the root node with the id of 0 from being printed, it needs to be ignored before outputting.

const arr = [
     {id: 3123124, parentId: 0o0000, title: 'title', type: 'block'},
     {id: 3542132, parentId: 3123124, title: 'title', type: 'child'},
     {id: 12345, parentId: 88888, title: 'title', type: 'block'},
     {id: 24124, parentId: 12345, title: 'title', type: 'child'},
     {id: 99999, parentId: 45324, title: 'title', type: 'child'},
     {id: 986648, parentId: 3123124, title: 'title', type: 'block'},
     {id: 77777, parentId: 88888, title: 'title', type: 'child'},
     {id: 54232, parentId: 3123124, title: 'title', type: 'child'},
     {id: 54308, parentId: 15075, title: 'title', type: 'child'},
     {id: 66666, parentId: 88888, title: 'title', type: 'block'},
     {id: 56445, parentId: 12345, title: 'title', type: 'child'},
     {id: 88888, parentId: 45324, title: 'title', type: 'block'},
     {id: 15075, parentId: 12345, title: 'title', type: 'block'},
     {id: 84356, parentId: 66666, title: 'title', type: 'child'},
     {id: 45324, parentId: 0o0000, title: 'title', type: 'block'},
];

// Create lookup table for efficient object lookups
const lookup = arr.reduce((acc, cur) => (acc.set(cur.id, cur), acc), new Map());

// Build tree with parentId as key and an array of child IDs as values
const mapping = arr.reduce((acc, cur) => {
  if(acc.has(cur.parentId)) acc.set(cur.parentId, [...acc.get(cur.parentId), cur.id])
  else acc.set(cur.parentId, [cur.id])
  return acc;
}, new Map());

// Exclude the root node
mapping.delete(0);

// Flatten keys and their values into a single structure using lookup table
const result = [...mapping.entries()].flatMap(([key, values]) => ([lookup.get(key), ...values.map(v => lookup.get(v))]));
console.log(JSON.stringify(result))
.as-console-wrapper { max-height: 100% !important; top: 0; }

The sequence of groups displayed may vary slightly from the shown example, but the groupings are accurate. For a specific order, you can sort [...mapping.entries()] prior to utilizing flatMap() to arrange the output accordingly.

Answer №4

In my strategy, I utilize a function that iterates recursively to retrieve children for each parent along with their descendants:

function getChildren(arr, parentId) {
  const children = arr.filter((item) => item.parentId === parentId);
  // Filter by child type, prioritizing "child" type first, followed by "block" type
  const childrenTypeChild = children.filter((child) => child.type === "child");
  const childrenTypeBlock = children.filter((child) => child.type === "block");
  const sortedChildrenByType = [...childrenTypeChild, ...childrenTypeBlock];

  // Recursively fetching children of each child
  return sortedChildrenByType.reduce((acc, child) => {
    acc.push(child, ...getChildren(arr, child.id));
    return acc;
  }, []);
}

const arr = [
  { id: 3123124, parentId: 0o0000, title: "title", type: "block" },
  { id: 3542132, parentId: 3123124, title: "title", type: "child" },
  { id: 12345, parentId: 88888, title: "title", type: "block" },
  { id: 24124, parentId: 12345, title: "title", type: "child" },
  { id: 99999, parentId: 45324, title: "title", type: "child" },
  { id: 986648, parentId: 3123124, title: "title", type: "block" },
  { id: 77777, parentId: 88888, title: "title", type: "child" },
  { id: 54232, parentId: 3123124, title: "title", type: "child" },
  { id: 54308, parentId: 15075, title: "title", type: "child" },
  { id: 66666, parentId: 88888, title: "title", type: "block" },
  { id: 56445, parentId: 12345, title: "title", type: "child" },
  { id: 88888, parentId: 45324, title: "title", type: "block" },
  { id: 15075, parentId: 12345, title: "title", type: "block" },
  { id: 84356, parentId: 66666, title: "title", type: "child" },
  { id: 45324, parentId: 0o0000, title: "title", type: "block" }
];

const parentIds = [...new Set(arr.map((item) => item.parentId))];
const parents = arr
  .filter((item) => parentIds.includes(item.id))
  .sort((parentA, parentB) => parentA.parentId - parentB.parentId);

const newArr = parents.reduce((acc, parent) => {
  // Check if the array is already sorted
  if (acc.length >= arr.length) {
    return acc;
  }

  // Recursively fetching children of each parent
  acc.push(parent, ...getChildren(arr, parent.id));
  return acc;
}, []);

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

Change the height of a div element using animation upon the button being clicked

I'm attempting to create an animation where a drop-down menu div expands from 0 to 250px in height when the menu button is clicked. I have been using jQuery for this purpose. Here is the relevant section of my HTML code: <script src="https://aja ...

The value retrieved from Axios is not properly set by React's useState function

My current challenge involves sending a GET request to http://localhost:8080/api/RE/template/${pageId} in order to display template items associated with a specific page ID. Upon checking the console, the following is displayed. https://i.sstatic.net/YCwe ...

Generate checkboxes by utilizing the JSON data

Here is a snippet of my JSON data: [ { "type": "quant", "name": "horizontalError", "prop": [ 0.12, 12.9 ] }, { "type": "categor", "name": "magType", "prop": [ ...

Utilizing AngularJS's $http.get to fetch video IDs and then combining them with the /embed endpoint through the ng

Currently, I am utilizing $http.get to retrieve a list of videos from YouTube using the API endpoint 'MY_API_KEY'. The goal is to construct a link with the video ID in the format: "{{videoID}}". However, extracting the videoID proves to be chall ...

Why is Javascript returning undefined in the alert message?

Why am I getting an undefined result in my alert when running the JavaScript function below: function tabs(data = null){ for(var i = 0; i< data.result.length ; i++){ $.each(data.result[i], function(key, value){ alert(data.result[i].key) ...

What is the process for decrypting the data that was encrypted using node-jose?

I am currently in the process of incorporating basic JOSE encryption and decryption functionalities by utilizing node-jose. This is my code implementation (using Node 8.2.1) const { JWE } = require('node-jose'); const jose = (publicKey, privat ...

Passing a Scope to ES6 Template Literals: How to Ensure they are Interpreted Correctly?

I've recently started utilizing template literals to create an error generator. Although I have functional code, I find myself having to define the list of potential errors within the constructor scope, and this is not ideal for me. Is there a way t ...

Looking through an array to find specific numbers and then displaying all associated variables

void findPopulationChange() { int userInput; int count = 0; System.out.println ("Please enter the population change to search for: "); userInput = scan.nextInt(); boolean isFound = false; for (count = 0; count < populati ...

Ways to ensure the bootstrap table header width aligns perfectly with the body width

I am having an issue with my bootstrap table where the header width is smaller than the body width because I set the table width to auto. How can I align the header and body widths? Here is a link to a plunker showcasing the problem. https://plnkr.co/edit ...

What are the different ways to share data between components in React?

There are two components in my code, one named <Add /> and the other named <Modal>. The <Add /> component has a state for handling click events. Whenever the <Add /> component is clicked, the state is set to true. I need to utilize ...

Accessing the "this" object in Vue.js components

When executing console.log(this) in the doSomething method, it returns "null". I was expecting console.log(this.currentPage) to display "main", but unfortunately, the "this" object is null.. :( Is there a way to access the value of "main" for currentPage ...

What is causing the browser to freeze in this infinite JavaScript loop?

I'm struggling to figure out why the browser is freezing up and getting stuck in the initial for loop within this function. Even when I try using arguments.length, it doesn't seem to log anything in the console. Take a look at the click function ...

Creating markers for every value in a table using Google Maps API v3

Looking for some guidance here. I have a table with multiple values that I need to process using a function. Could someone help me with a suitable loop in jQuery or JavaScript that can achieve this? I'm still learning the ropes of these languages. My ...

When using React / Next.js, the .map() function may not rerender the output based on changes in the state array if the keys remain the same

In my react component, Matches.js, I handle the display of tournament matches sorted by rounds. The data is fetched from a parent component through nextjs SSR and passed as props to the child component. To avoid unnecessary requests upon data changes like ...

Combining and adding together numerous objects within an array using JavaScript

I'm looking to combine two objects into a single total object and calculate the percentage change between the two values. I'm encountering some difficulties while trying to implement this logic, especially since the data is dynamic and there coul ...

How can I optimize my .find query for a MongoDB GET request to achieve maximum performance?

I'm struggling to figure out how to retrieve only the last item stored in my database using a GET request. While I can successfully get the desired output in the mongo shell (as shown below), I haven't been able to replicate it in my GET route qu ...

Exploring the dynamic shift with jQuery .find

Currently, I am in the process of learning jQuery and have come across a simple issue. My goal is to change the text within a div when a link is clicked. The div in question is a duplicate of another div on the page, and my intention is to remove the copy ...

Can you explain the functionality of the execCommand "insertBrOnReturn" feature?

I ran some code in Chrome and encountered an unexpected behavior: document.execCommand("insertBrOnReturn", false, true); http://jsfiddle.net/uVcd5/ Even though I've tried setting the last parameter to true or false, the outcome remains the same: ne ...

What is the best way to establish a maximum value for variable inputs?

In my Vue form, I have dynamic inputs for issuing shares to shareholders. The user first registers the total amount of shares in the form, then starts issuing this total amount partially by adding dynamic inputs as needed. Finally, the form is submitted. M ...

Turn off Affix feature for mobile phones

Hello, I created a sidebar and used some JavaScript to automatically update its width in relation to its parent container. Now, I want the sidebar to be removed automatically when the window size goes below 750px (which I believe is the tablet breakpoint i ...