1

This should be the input array

var a = [2,1,3,4,1,[4,6,2,4],2,4,1];

For the output i have two cases :- (index of internal array is not changing)

a = [1,1,2,3,4,[2,4,4,6],1,2,4]

and

a = [1,1,1,2,2,[2,4,4,6],3,4,4]

This is what i am trying to use :-

a.sort(function(a,b){
  if(b instanceof Array){
    b.sort();
  }
})
Ankur Marwaha
  • 1,613
  • 2
  • 15
  • 30

6 Answers6

1

You can loop over array and remove all sub arrays and save their index and then sort the new array and again push sorted sub arrays on specific indexes.

Sample

var arr = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 4, 1];
var arr1 = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 6, 4, [4, 5, 3], 1, 2, 1, 3]
var a = [2,1,3,4,1,[4,6,[4,5,[7,3,2,1,6],1,2],2,4],2,4,1];

function mySort(arr) {
  var _list = [];
  arr.forEach(function(item, index) {
    if (Array.isArray(item)) {
      _list.push({
        index: index,
        value: arr.splice(index, 1).pop()
      });
    }
  });

  arr.sort();

  _list.forEach(function(item) {
    arr.splice(item.index, 0, mySort(item.value))
  })
  return arr;
}

console.log(mySort(arr.slice()))
console.log(mySort(arr1.slice()))
console.log(mySort(a.slice()))

Edit 1

Inspired from joey-etamity's answer, have made it generic for nested structure.

Community
  • 1
  • 1
Rajesh
  • 24,354
  • 5
  • 48
  • 79
1

This is the perfect solution, use nested function invoke to sort array.

Firstly , store all the array position and sub array.

Secondly, extract numbers into new array,

Finally insert sorted array into same position as before.

var a = [2,1,3,4,1,[4,6,[4,5,[7,3,2,1,6],1,2],2,4],2,4,1];

function nestedSort(arr){
   var items = []; 
   var numArr = [];
    for ( key in arr){
      if (arr[key] instanceof Array)
      {
          items.push({index:key,array:arr[key]});
      }else{
          numArr.push(arr[key]);
      }
    }
   numArr.sort();
   for (key in items){
    numArr.splice(items[key].index,0,nestedSort(items[key].array));
   }
   
  return numArr;
}

console.log(nestedSort(a));
[
  1,
  1,
  1,
  2,
  2,
  [
    2,
    4,
    [
      1,
      2,
      [
        1,
        2,
        3,
        6,
        7
      ],
      4,
      5
    ],
    4,
    6
  ],
  3,
  4,
  4
]

Hope this can solve your problem. :)

Joey Etamity
  • 856
  • 4
  • 9
1

Array.sort() is not built to handle partial Arrays, what you would need in your case, but we can work around this problem by pre-processing the data (wrapping it with additional information), then sorting and at the end, extracting the original values:

case 1: sorting the parts between the Arrays
[2,1,3,4,1,[4,6,2,4],2,4,1] -> [1,1,2,3,4,[2,4,4,6],1,2,4]

function sort1(arr){
    //I add an artificial "property" of to the values, to "describe" the groups, and to be able to sort by
    //each Array is it's own group (so they stay in order), and the values in between share the same group
    var group = 0, 
        isArray = false;

    //an intermediate Array holding all the information (in order) to either apply it to the current Array, or to return (map) it as a new Array
    var intermediate = arr.map(function(v,i){
        //last value was an Array, this is the first value after an Array, start a new group
        if(isArray) ++group;    

        if(isArray = Array.isArray(v)){ //update isArray
            v = sort1(v);               //recursive sorting
            ++group;                    //the last group just ended here
        }

        //return a composition, that contains all the data I need to sort by
        return {
            group: group,
            value: v
        }
    }).sort(function(a, b){
        //forst sort by group, and (only) if two values share the same group, sort by the original value
        return a.group - b.group || a.value - b.value
    });

    //apply data to current Array
    intermediate.forEach(function(obj, i){ arr[i] = obj.value });
    return arr;

    //return new Array
    //return intermediate.map(function(obj){ return obj.value });
}

case 2: treating an Array like it's first value
[2,1,3,4,1,[4,6,2,4],2,4,1] -> [1,1,1,2,2,[2,4,4,6],3,4,4]

function sort2(arr){
    //an utility to fetch the first non-array value recursively
    function _value(v){ 
        while(Array.isArray(v)) v = v[0];
        return v;
    }

    var intermediate = arr.map(function(v, i){
        if(Array.isArray(v)) v = sort2(v);
        return {
            index: i,
            value: v,
            sortingValue: _value(v)
        }
    }).sort(function(a, b){
        return a.sortingValue - b.sortingValue || a.index - b.index;
    });

    //apply data to current Array
    intermediate.forEach(function(obj, i){ arr[i] = obj.value });
    return arr;

    //return new Array
    //return intermediate.map(function(obj){ return obj.value });
}
Thomas
  • 11,958
  • 1
  • 14
  • 23
  • This is what i wanted as the output, but i am still looking for an easier way to achieve this. :) – Ankur Marwaha Jul 30 '16 at 19:37
  • @AnkurMarwaha What do you consider complicated here? precomputing the values and unpacking them at the end? This is as cheap as it could be, if you would/could put the very same code in the sorting-function it would be called at least twice as often (for a sorted Array, way more if it is not) – Thomas Jul 31 '16 at 00:17
0

No, you don't put the sort call in the comparison function. You would recurse through your arrays, bottom to top, and sort them one after the other. In your case you might not even need recursion if it's only one array in another:

a.forEach(function(element) {
   if (Array.isArray(element))
       element.sort(function compare(a, b) { return a-b; });
})

(I've chosen a simple numerical compare here).

Then you'd sort the outer array:

a.sort(function compare(a, b) {
    if (Array.isArray(a)) a = a[0];
    if (Array.isArray(b)) b = b[0];
    return a - b;
})

(here compare takes the first element of the array to compare by that against the other numbers).

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

I suggest to splice the array if there is an element an array. Then sort the array and reassemble the array.

This proposal iterates from the back and keeps the array intact while splicing.

function sort(array) {
    var i = array.length,
        inside = [];

    while (i--) {
        if (Array.isArray(array[i])) {
            inside.unshift({ pos: i, value: sort(array.splice(i, 1)[0]) });
        }
    }
    array.sort(function (a, b) { return a - b; });
    inside.forEach(function (a) {
        array.splice(a.pos, 0, a.value);
    });
    return array;
}
var a = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 4, 1];

console.log(sort(a));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
-1

I think this would be better to use Array.prototype.sort this way:

// var arr = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 4, 1];
var arr = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 6, 4, [4, 5, 3], 1, 2, 1, 3];
var chunks = chunkate(arr)
console.log(JSON.stringify(chunks));

chunks.forEach(ch => ch.sort(_sort));

var result = chunks.reduce((p, c) => p.concat(c));
console.log(JSON.stringify(result));

function _sort(a, b) {
  var isAa = Array.isArray(a),
    isAb = Array.isArray(b);
  isAb && b.sort(_sort);

  return (isAa || isAb) ? 0 : a - b;
}

function chunkate(arr) {
  return arr.reduce((a, c) => {
    Array.isArray(c) ? a.push(chunkate(c), []) : a[a.length - 1].push(c)
    return a;
  }, [[]]);
}

How it works?

If items to compare are are array then they shouldn't be replaced so by sending false sort function recognize that there is no need to replace. Otherwise the simple compare is the answer.

Edit

As discussed in comments, it's better to separate values to chunks and then sort each part then join parts again. If nesting depth is only one level you can use default sort (without _sort function) but be aware of array in array used for nested array. So the sort should be changed like this:

chunks.forEach(ch => Array.isArray(ch[0])? ch[0].sort(): ch.sort());
Morteza Tourani
  • 3,506
  • 5
  • 41
  • 48
  • arr = [2, 1, 3, 4, 1, [4, 6, 2, 4], 2, 6, 4,[4,5,3], 1,2,1,3]; -- this is giving the wrong output -- [1,1,1,1,2,2,2,3,[2,4,4,6],[3,4,5],3,4,4,6] – Ankur Marwaha Jul 24 '16 at 13:05
  • Does this solution make some assumptions about the underlying sort algorithm? Isn't returning `false` equivalent to returning `0`? – nnnnnn Jul 24 '16 at 13:13
  • can you please try to use the input array in the above comment ? – Ankur Marwaha Jul 24 '16 at 13:15
  • The return value of the `sort` function should be a number, not a boolean. Returning `false` doesn’t make sense and is indeed interpreted as a `0`, so just return `0` instead of `false`. – Sebastian Simon Jul 24 '16 at 13:22
  • 1
    Returning `false` from a comparison function hardly makes sense and [is actually wrong here](https://stackoverflow.com/q/24080785/1048572) – Bergi Jul 24 '16 at 13:24
  • @Xufox try this: `true - false` – Morteza Tourani Jul 24 '16 at 13:30
  • @mortezaT I’m not sure what you’re trying to say with your reply to me. – Sebastian Simon Jul 24 '16 at 13:32
  • @nnnnnn `false` means `0` hear. which shows equality. – Morteza Tourani Jul 24 '16 at 13:32
  • @Xufox If `a === b` then result of comparison should be `0` which is the same value as `false` – Morteza Tourani Jul 24 '16 at 13:34
  • @mortezaT: Yes it does result in the same thing as `return 0`. And that is wrong, making any array equal to everything. That can't work out. – Bergi Jul 24 '16 at 13:36
  • @Bergi I want the array to stay in it's place. what you suggest? – Morteza Tourani Jul 24 '16 at 13:38
  • @mortezaT: There is no "stay in place" in a comparison. Like the other answers suggested, remove the arrays before, sort, and put them back after. – Bergi Jul 24 '16 at 13:40
  • @mortezaT Of course, `false` is being treated as `0`, but it still doesn’t make sense as a return value of a sorting function, as it is _semantically_ wrong. You could as well write `.sort((a, b) => null)` or `.sort((a, b) => "I like toast.")` but those just don’t make sense! – Sebastian Simon Jul 24 '16 at 13:41
  • @Bergi That wouldn't work this situation because every part should be sorted individually and so my answer seems to be OK now. – Morteza Tourani Jul 24 '16 at 13:53
  • @Xufox That's right and no doubt about it. I changed that to 0 before our discussion. BTW `I Like Fesenjoon` ;) – Morteza Tourani Jul 24 '16 at 13:57
  • @mortezaT: If every part should be sorted individually, put them in their own arrays and sort those. Or tag each value with the "part" it is in (though that's hard with primitive numbers) so that you could compare them by their part indices. In either case, you wouldn't need to compare arrays against something else any more. – Bergi Jul 24 '16 at 13:59
  • @Bergi I Think this can be a better approach but as `0` is needed for deeper arrays, I didn't find any change for it. – Morteza Tourani Jul 25 '16 at 08:59