简体   繁体   中英

Can i sort nested array using nested sort()?

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();
  }
})

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.

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)); 

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. :)

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 });
}

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).

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());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM