The Ultimate Guide to Sorting Multi-dimensional Javascript Arrays with Complex Numerical Values

I need to organize this array:

const array = [
  [18, [18, 16], 16],
  15,
  [18, 19, 51],
  [[18, 16], 15, [14, 13]],
  [10, 9, 20],
  99,
  49,
  [11, [22, 10], [[10, 9], 11, 9, [1, 2]], 100, 72],
  [[11], [[19, 14], [77, 18]]]
];

Arrange this array in the following way:

  1. All single elements within the array should come first

  2. Next, include one-dimensional arrays

  3. Then, two-dimensional arrays

  4. And so on for higher dimensions

  5. Ensure all elements are sorted within each sub-array.

The expected result should look like this,

[
  15,
  49,
  99,
  [18, 19, 51],
  [9, 10, 20],
  [16, 18, [16, 18]],
  [15, [16, 18], [13, 14]],
  [11, 72, 100, [10, 22], [9, 11, [9, 10], [1, 2]]],
  [[11], [[14, 19], [18, 77]]]
]

This implementation works up to four-dimensional arrays. However, it won't handle arrays with dimensions greater than four (n > 4). How can we optimize the code to achieve results for any n-dimensional array?

const array = [
    [ 18, [18,16], 16], 
    15, 
    [18, 19, 51], 
    [[18, 16], 15, [14, 13]], 
    [10,9,20], 
    99, 
    49, 
    [11, [22,10], [[10,9], 11, 9, [1,2]], 100, 72],
    [[11], [[19, 14], [77, 18]]]
];

function getArrayDepth(value) {
    return Array.isArray(value) ?
        1 + Math.max(0, ...value.map(getArrayDepth)) :
        0;
}

function sort_array1(arr) {
    for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] > arr[j]) {
                const temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
    return arr;
}

function sort_array2(arr) {
    for (let j = 0; j < arr.length; j++) {
        arr[j] = sort_array1(arr[j]);
    }

    return arr;
}

function sort_array3(arr) {
    for (let j = 0; j < arr.length; j++) {
        let array1 = [];
        let array2 = [];

        for (let k = 0; k < arr[j].length; k++) {
            if (getArrayDepth(arr[j][k]) == 0) {
                array1.push(arr[j][k]);
            }
            else {
                array2.push(arr[j][k]);
            }
        }

        array1 = sort_array1(array1);
        array2 = sort_array2(array2);
        arr[j] = array1.concat(array2);
    }

    return arr;
}

function sort_array4(arr) {
    for (let j = 0; j < arr.length; j++) {
        let array1 = [];
        let array2 = [];
        let array3 = [];

        for (let k = 0; k < arr[j].length; k++) {
            if (getArrayDepth(arr[j][k]) == 0) {
                array1.push(arr[j][k]);
            }
            else if (getArrayDepth(arr[j][k]) == 1) {
                array2.push(arr[j][k]);
            }
            else {
                array3.push(arr[j][k]);
            }
        }

        array1 = sort_array1(array1);
        array2 = sort_array2(array2);
        array3 = sort_array3(array3);
        arr[j] = array1.concat(array2, array3);
    }

    return arr;
}

function sequenced_array(arr) {
    let array1 = [];
    let array2 = [];
    let array3 = [];
    let array4 = [];

    for (let i = 0; i < arr.length; i++) {
        if (getArrayDepth(arr[i]) == 0) {
            array1.push(arr[i]);
        }
        else if (getArrayDepth(arr[i]) == 1) {
            array2.push(arr[i]);
        }
        else if (getArrayDepth(arr[i]) == 2) {
            array3.push(arr[i]);
        }
        else {
            array4.push(arr[i]);
        }
    }

    array1 = sort_array1(array1);
    array2 = sort_array2(array2);
    array3 = sort_array3(array3);
    array4 = sort_array4(array4);

    array1 = array1.concat(array2, array3, array4);
    return array1;
}

let sortedArray = sequenced_array(array);

/*for (let i = 0; i < sortedArray.length; i++) {
    console.log(sortedArray[i]);
}*/

console.log(sortedArray)

Answer №1

CAUTION: The custom sorting algorithm is resource-intensive. It may have time and space complexities that could pose challenges with very large or deeply nested arrays.

We welcome any alternative solutions that might offer better efficiency

const getDepth = (item) => !Array.isArray(item) ? 0 : 1 + Math.max(...item.map(getDepth), 0);

const customSort = (a, b) =>  {
  const depthA = getDepth(a);
  const depthB = getDepth(b);
  if (depthA !== depthB) return depthA - depthB;
  if (Array.isArray(a) && Array.isArray(b)) {
    for (let i = 0; i < Math.min(a.length, b.length); i++) {
      const comp = customSort(a[i], b[i]);
      if (comp !== 0) return comp;
    }
    return a.length - b.length;
  }
  if (!Array.isArray(a) && !Array.isArray(b)) return a - b;
  return Array.isArray(a) ? 1 : -1;
}

const sortArray = (array) => {
  // only sort arrays
  if (!Array.isArray(array)) return array;

  // recursively sort arrays
  const deeplySorted = array.map(item => Array.isArray(item) ? sortArray(item) : item);

  // Then, sort by depth and value
  return deeplySorted.sort(customSort);
}

console.log(sortArray(array));
<script>
const array = [
    [18, [18, 16], 16],
    15,
    [18, 19, 51],
    [[18, 16], 15, [14, 13]],
    [10, 9, 20],
    99,
    49,
    [11, [22, 10], [[10, 9], 11, 9, [1, 2]], 100, 72],
    [[11], [[19, 14], [77, 18]]],
    [[[[3],[2],[2,3,1]]]] // Added this deeply nested array
];
</script>

Answer №2

I tackled this challenge by using recursion,

let complexArray = [
  [18, [18, 16], 16],
  15,
  [18, 19, 51],
  [[18, 16], 15, [14, 13]],
  [10, 9, 20],
  99,
  [[1, [20, [9, 3], 1], [90, 12], 90, 13], 26, [10, 2], 200, 100],
  49,
  [11, [22, 10], [[10, 9], 11, 9, [1, 2]], 100, 72],
  [
    [11],
    [
      [19, 14],
      [77, 18],
    ],
  ],
];

//Determine the depth of the provided array
function getArrayDepth(value) {
  return Array.isArray(value)
    ? 1 + Math.max(0, ...value.map(getArrayDepth))
    : 0;
}

function sortArray(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] > arr[j]) {
        const temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
      }
    }
  }
  return arr;
}

//Recursively sort the complex array
function sortComplexArray(arr) {
  let newArray = [];
  let depth = getArrayDepth(arr);

  for (let d = 0; d <= depth; d++) {
    let tempArray = [];
    for (let i = 0; i < arr.length; i++) {
      if (getArrayDepth(arr[i]) == d) {
        tempArray.push(arr[i]);
      }
    }

    if (d == 0) {
      tempArray = sortArray(tempArray);
    }
    newArray = newArray.concat(tempArray);
  }

  arr = newArray;

  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      arr[i] = sortComplexArray(arr[i]);
    }
  }

  return arr;
}

let sortedArray = sortComplexArray(complexArray);

console.log(sortedArray);

Appreciation to all who contributed to solving the problem!

Answer №3

  1. explore to identify depths within arrays and potentially duplicate the array
  2. arrange based on calculated depths

const array = [
  [18, [18, 16], 16],
  15,
  [18, 19, 51],
  [[18, 16], 15, [14, 13]],
  [10, 9, 20],
  99,
  49,
  [11, [22, 10], [[10, 9], 11, 9, [1, 2]], 100, 72],
  [[11], [[19, 14], [77, 18]]]
];

const sortByDepth = (array, clone = true) => {

  const depths = new Map;

  const scan = arr => {
    let depth = 0;
    if(Array.isArray(arr)){
      depth = Math.max(...arr.map(scan));
      depths.set(arr, depth++);
    }
    return depth;
  };
  
  const scanClone = arr => {
    if(Array.isArray(arr)){
      const mapped = arr.map(scanClone);
      let {cloned, depth} = mapped.reduce((r, [arr, depth]) => (r.cloned.push(arr), r.depth < depth && (r.depth = depth), r), {cloned:[], depth:-Infinity});
      depths.set(cloned, depth++);
      return [cloned, depth];
    }
    return [arr, 0];
  };
  
  clone ? [array] = scanClone(array) : array.forEach(scan);

  const sort = arr => {
    arr.sort((a, b) => {
      const da = depths.get(a) ?? -1;
      const db = depths.get(b) ?? -1;
      if(da === -1 && db === -1){
        return a - b;
      }
      if(da > db) return 1;
      if(da < db) return -1;
      return 0;
    });
    arr.forEach(v => Array.isArray(v) && sort(v));
  }

  sort(array);
  return array;
}

sortByDepth(array).forEach(v => console.log(JSON.stringify(v)));
.as-console-wrapper{
  max-height: 100% !important;
}

` Chrome/122
----------------------------------------------------------------------------
>                n=9       |      n=90      |     n=900      |    n=9000    
Alexander   1.00x x10k 122 | 1.00x  x1k 125 | 1.00x x100 134 | 1.00x x10 209
mplungan    3.89x x10k 474 | 8.80x x100 110 | 9.25x  x10 124 | 6.08x  x1 127
----------------------------------------------------------------------------
https://github.com/silentmantra/benchmark `
` Firefox/124
---------------------------------------------------------------------------
>                n=9       |     n=90      |     n=900      |    n=9000    
Alexander   1.00x x10k 152 | 1.00x x1k 150 | 1.00x x100 168 | 1.00x x10 224
mplungan    1.95x x10k 296 | 5.29x x1k 794 | 7.62x  x10 128 | 8.04x  x1 180
---------------------------------------------------------------------------
https://github.com/silentmantra/benchmark `

const $chunk = [
  [18, [18, 16], 16],
  15,
  [18, 19, 51],
  [[18, 16], 15, [14, 13]],
  [10, 9, 20],
  99,
  49,
  [11, [22, 10], [[10, 9], 11, 9, [1, 2]], 100, 72, [
      [18, [18, 16], 16],
      15,
      [18, 19, 51],
      [[18, 16], 15, [14, 13]],
      [10, 9, 20],
      99,
      49,
      [11, [22, 10], [[10, 9], 11, 9, [1, 2]], 100, 72],
      [[11], [[19, 14], [77, 18]]]
    ]],
  [[11], [[19, 14], [77, 18]]]
];

const $input = [];
const array = $input;

// @benchmark mplungan
const getDepth = (item) => !Array.isArray(item) ? 0 : 1 + Math.max(...item.map(getDepth), 0);

const customSort = (a, b) =>  {
  const depthA = getDepth(a);
  const depthB = getDepth(b);
  if (depthA !== depthB) return depthA - depthB;
  if (Array.isArray(a) && Array.isArray(b)) {
    for (let i = 0; i < Math.min(a.length, b.length); i++) {
      const comp = customSort(a[i], b[i]);
      if (comp !== 0) return comp;
    }
    return a.length - b.length;
  }
  if (!Array.isArray(a) && !Array.isArray(b)) return a - b;
  return Array.isArray(a) ? 1 : -1;
}

const sortArray = (array) => {
  // only sort arrays
  if (!Array.isArray(array)) return array;

  // recursively sort arrays
  const deeplySorted = array.map(item => Array.isArray(item) ? sortArray(item) : item);

  // Then, sort by depth and value
  return deeplySorted.sort(customSort);
}

// @run
sortArray(array);

// @benchmark Alexander

const sortByDepth = (array, clone = true) => {

  const depths = new Map;

  const scan = arr => {
    let depth = 0;
    if(Array.isArray(arr)){
      depth = Math.max(...arr.map(scan));
      depths.set(arr, depth++);
    }
    return depth;
  };
  
  const scanClone = arr => {
    if(Array.isArray(arr)){
      const mapped = arr.map(scanClone);
      let {cloned, depth} = mapped.reduce((r, [arr, depth]) => (r.cloned.push(arr), r.depth < depth && (r.depth = depth), r), {cloned:[], depth:-Infinity});
      depths.set(cloned, depth++);
      return [cloned, depth];
    }
    return [arr, 0];
  };
  
  clone ? [array] = scanClone(array) : array.forEach(scan);

  const sort = arr => {
    arr.sort((a, b) => {
      const da = depths.get(a) ?? -1;
      const db = depths.get(b) ?? -1;
      if(da === -1 && db === -1){
        return a - b;
      }
      if(da > db) return 1;
      if(da < db) return -1;
      return 0;
    });
    arr.forEach(v => Array.isArray(v) && sort(v));
  }

  sort(array);
  return array;
}
// @run
sortByDepth(array);

/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));
.as-console-wrapper{
  max-height: 100% !important;
}

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

Difficulties with choosing predecessors

Is there a way in the HTML snippet below to extract the checkall from the thing? .. <input type="checkbox" class="checkall"><label>Check all</label> <ul> <li><input type="checkbox" class="thing"><label>Thing 1 ...

Having trouble getting Visual Studio Code to execute my build command in launch.json

Whenever I run this specific command in a command prompt, everything works perfectly: browserify -u jquery /public/index.js -t babelify -o /public/scripts/bundle.js & node /server.js But when attempting to integrate it into the program section of my ...

Loading data from external URLs using AJAX and JSON

I am facing an issue, can someone please help? I have two websites and I want to send data between them. First website: var site_adres = $(location).attr('href'); var myArray = site_adres.split('/'); var site_adi_al = myArray[2]; ...

Validating forms in ReactJS

I have developed a basic form validation feature for React. The inspiration for this project came from the following source: When I try to submit the form, input errors arise within the isValid function. I am seeking assistance in resolving this issue. A ...

Is Babel necessary for enabling JavaScript compatibility in my TypeScript React project, excluding Create React App?

This is the webpack configuration for my React project built in TypeScript, module.exports = { mode: 'development', entry: ['./src/main.tsx'], module: { rules: [ { // Rule for ts/tsx files only, no rule for js/js ...

What is the best way to set up v-models for complex arrays or nested objects?

I'm looking to efficiently create multiple v-models for random properties within a deep array, where the position of attributes in arrays/objects can change dynamically. While I've managed to achieve my goal with the current setup, I'll need ...

Unlocking Controller Functions in AngularJS Directives: A Step-by-Step Guide

Here is a sample controller and directive code: class DashboardCtrl { constructor ($scope, $stateParams) { "ngInject"; this.$scope = $scope; this.title = 'Dashboard'; } loadCharts () { // some logic here } } export def ...

Utilizing scroll functionality within a DIV container

I have the following Javascript code that enables infinite scrolling on a webpage. Now, I am looking to implement this feature within a specific DIV element. How can I modify this code to achieve infinite scroll functionality inside a DIV? Any assistance ...

What is the best approach to generate and organize 100 random numbers in a program, ensuring they are sorted in ascending order from 1 to 100?

I have successfully implemented the sorting and printout functionality of the program. However, I am now facing a challenge in generating 100 random numbers between 1 and 100, sorting them. I have created an array to store the generated numbers, and I ha ...

Button style not being affected by CSS

I'm having trouble changing the background color of a button in CSS. The CSS link is working perfectly fine, and the button changes color when I attempt to apply Bootstrap or use DOM to change it, but not when I use pure CSS. Here's the code sni ...

Issues with Bootstrap-slider.js functionality

I'm having trouble getting the bootstrap-slider.js library from to function correctly. All I see are textboxes instead of the slider components. You can view an example at I have verified that the CSS and JS files are pointing to the correct direc ...

Rotate Chevron icon downwards on mobile devices in a Multilevel Dropdown Bootstrap 4

Bootstrap 4 is being utilized for my current project, I am looking to modify the rotation of the chevron icon in a multi-level dropdown menu specifically on mobile devices when the parent link is tapped, Here is the code snippet for the multi-level dropd ...

Error: The 'getters' property is undefined in @vue/test-utils and cannot be read

I've been utilizing @vue-test-utils for unit testing in VueJS. This is what my store setup looks like: export default { root: true, state: { batches: [] }, getters: { getBatches: state => { return state.batches } } } The componen ...

Repeat a command in Discord.js indefinitely

Looking for help with this discord.js code I have. Currently, when someone runs the command "!cat", it sends a random image from r/cats. Here is the snippet: var Discord = require('discord.js'); var bot = new Discord.Client() randomPuppy = requir ...

A method to iterate through arrays and retrieve the values of the one that meets the specified condition

I am working on a textarea feature that submits text and converts it into an array using the explode function. The values of that array are then turned into individual arrays within a foreach loop. Here is the function in question: The textarea entry ...

Angular 4: Transform a string into an array containing multiple objects

Recently, I received an API response that looks like this: { "status": "success", "code": 0, "message": "version list", "payload" : "[{\"code\":\"AB\",\"short\":\"AB\",\"name\":\"Alberta&b ...

ApEditingError: The method editAp is not defined in _this.props

I am facing an issue while trying to invoke the function editAp located in the func.js file from Edit1.js. I have provided the code snippets below for better understanding: import firebase from "firebase"; if (!firebase.apps.length) { firebase.initializ ...

Having difficulty choosing a drop-down menu option with AJAX technology

My current project involves creating a page where the database is updated upon a drag-and-drop event. Specifically, I have implemented drag-and-drop functionality on a page, and whenever a div (deal-card) is dragged and dropped into another div (stage-colu ...

Creating responsive arrow heads using D3: A step-by-step guide

Currently, I am working on creating a thermometer-style bar chart using D3.js version 3. While I have successfully created a basic responsive rectangle with two filled colors, I am facing difficulties in figuring out how to add arrow heads to the design. B ...

Linking two div elements together with a circular connector at the termination point of the line

I am currently working on designing a set of cards that will showcase a timeline. I envision these cards to be connected by lines with circles at each end, for a visually appealing effect. At the moment, I have created the cards themselves but I am struggl ...