2

I have an array of numeric arrays.

Each numeric array is always an ascending sequence, eg [8, 9, 10].

I want to sort each numeric array based on:

  1. The first (and lowest) number in the array.
  2. Their range.

For example, given the following:

[
  [1],
  [2, 3],
  [10],
  [11, 12, 13],
  [3, 4],
  [2, 3, 4],
  [1, 2, 3, 4, 5, 6],
  [10, 11, 12],
  [7, 8, 9],
  [1, 2, 3],
  [10, 11],
]

The result should be:

[
  [1],
  [1, 2, 3],
  [1, 2, 3, 4, 5, 6],
  [2, 3],
  [2, 3, 4],
  [3, 4],
  [7, 8, 9],
  [10],
  [10, 11],
  [10, 11, 12],
  [11, 12, 13],
]

The result is very nearly achieved using:

arr.sort((a, b) => a[0] - b[0]);

but not quite.

Here is my example code: https://repl.it/repls/MadeupMediumpurpleCavy

Is there a way I can achieve this using Array.prototype.sort?

3 Answers 3

2

Currently your code only sorts the arrays by looking at the first element of each array via the compareFunction in your call to sort:

const actual = difficulties.sort((a, b) => a[0] - b[0])

If the values of the first element are equal, for example:

[2, 3, 4]

[2, 3]

the expected resut is to keep them in the same order in which they currently are, BUT NO! Through some wibbly wobbly timey wimey... stuff... there are certain instances where they are switched if the resulting statement resolves to 0. These instances as far as I can tell are unpredictable and may or may not be related to specific browsers.

So at this point the known result of the sort function as we are using it here is as follows:

a[0] - b[0] < 0: do nothing
a[0] - b[0] = 0: undef (maybe move maybe not)
a[0] - b[0] > 0: move element b to before element a

With that said, there needs to be a condition for the range to be sorted if the first element of the arrays (a and b) is the same. Luckily in your set of data the range directly corresponds to the length of the array so it makes things a litte simpler.

This is a good point to mention logical operators do not have to be exclusively used with boolean values in javascript. Integers can also be used! With different data types than boolean the results can become slightly confusing but this is what is important in this specific example:

The only integer that is falsy is 0! All non 0 integers are truthy.

SO...

OR short circuits if the first value is true, meaning in this case that if the left statement resolves to a non 0 the OR statement returns that non 0 integer and does not continue on to evaluate the right statement.

Consider the following:

a[0] - b[0] || a.length - b.length

using the example arrays:

[1, 2, 3]

[2, 3]

resulting values would be:

a[0] = 1
b[0] = 2

a[0] - b[0] = -1

Therefore:

a[0] - b[0] || a.length - b.length

resolves to -1 || doesn't matter (1)

-1 is non 0 so this OR statement returns -1 to the sort function which keeps the elements where they are.

As opposed to the following:

[2, 3, 4]

[2, 3]

resulting values would be:

a[0] = 2
b[0] = 2

a[0] - b[0] = 0

Therefore:

a[0] - b[0] || a.length - b.length

resolves to 0 || 1

This OR statement returns 1 because 0 is false and 1 is true.

1 is a positive number which to the sort function means (as stated above) to bring element b behind element a.

Tried to be as detailed as possible. Hope this helps!

Sign up to request clarification or add additional context in comments.

Comments

2

You need to map your array and sort each element, then sort the outer array.

yourArray
  .map(arr => arr.sort()) // Sort each subarray
  .sort((a,b) => a[0]-b[0] ? a[0]-b[0] : a.length - b.length) // Sort the outer array 

We sort the outer array according to the first element of each subarray, if the first elements of the compared subarrays are equals then the difference is 0, in this case we fallback in the second part of the condition and we apply a sort according to the length.

A slightly shorter version is this:

yourArray
  .map(arr => arr.sort())
  .sort((a,b) => a[0]-b[0] || a.length - b.length) 

Here is the demo:

var arr = [
  [1],
  [2, 3],
  [10],
  [11, 12, 13],
  [3, 4],
  [2, 3, 4],
  [1, 2, 3, 4, 5, 6],
  [10, 11, 12],
  [7, 8, 9],
  [1, 2, 3],
  [10, 11],
];

var sorted = arr.map(arr => arr.sort())
  .sort((a, b) => a[0] - b[0] || a.length - b.length);

console.log(sorted)

1 Comment

I added a running snippet, feel free to roll it back.
1

In your case, comparing just the first elements and lengths as in the other answer is the most efficient:

var arr = [ [1], [2, 3], [10], [11, 12, 13], [3, 4], [2, 3, 4], 
            [1, 2, 3, 4, 5, 6], [10, 11, 12], [7, 8, 9], [1, 2, 3], [10, 11] ]

arr.sort((a, b) => a[0] - b[0] || a.length - b.length)

console.log( JSON.stringify(arr).replace(/],/g, '],\n ') )

but for completeness, I am adding more generic array comparison method just in case:

var arr = [ [1], [2, 3], [10], [11, 12, 13], [3, 4], [2, 3, 4], 
            [1, 2, 3, 4, 5, 6], [10, 11, 12], [7, 8, 9], [1, 2, 3], [10, 11] ]

function compareArrays(a, b) { 
  for (var len = Math.min(a.length, b.length), diff, i = 0; i < len; i++)
     if (diff = a[i] - b[i]) return diff
   return a.length - b.length
}

arr.sort(compareArrays)

console.log( JSON.stringify(arr).replace(/],/g, '],\n ') )

1 Comment

What was your comment on sorting on my answer again?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.