How to Merge sorted Arrays in JavaScript

后端 未结 5 959
北恋
北恋 2020-12-17 16:58

I have three sorted arrays like below

[{name:\"a\"}, {name:\"b\"}, {name:\"m\"}, {name:\"x\"}]
[{name:\"a\"}, {name:\"e\"}, {name:\"i\"}, {name:\"o\"}]
[{n         


        
相关标签:
5条回答
  • 2020-12-17 16:59

    Edited to reflect that Exception's original solution, extended by calling it like mergeSorted(mergeSorted(a,b),c) is faster than my solution here.


    Javascript's builtin sort is [not] fast enough that you can just concatenate all the arrays together and sort the entire thing in one go. Javascript is not good for re-implementing things that should be done lower level.

    var a1 = [{name:"a"}, {name:"b"}, {name:"m"}, {name:"x"}]
    var a2 = [{name:"a"}, {name:"e"}, {name:"i"}, {name:"o"}]
    var a3 = [{name:"g"}, {name:"h"}, {name:"m"}, {name:"n"}]
    
    a1.concat(a2,a3).sort(function(a,b){return (a.name>b.name)-(a.name<b.name)})
    // [{name:"a"}, {name:"a"}, {name:"b"}, {name:"e"}, {name:"h"}, {name:"i"}, {name:"g"}, {name:"m"}, {name:"m"}, {name:"n"}, {name:"o"}, {name:"x"}]
    
    0 讨论(0)
  • 2020-12-17 17:11

    Update:

    Seeing as it is current_year this would now be:

    const mergeAll = (...arrays) => arrays.reduce(mergeSorted);
    

    Original:

    If you're feeling functional this is a perfect place to use reduce.

    var mergeAll = function(){
        return Array.prototype.slice.call(arguments).reduce(mergeSorted);
    };
    

    example:

    var a = [{name:"a"}, {name:"b"}, {name:"m"}, {name:"x"}];
    var b = [{name:"a"}, {name:"e"}, {name:"i"}, {name:"o"}];
    var c = [{name:"g"}, {name:"h"}, {name:"m"}, {name:"n"}];
    
    console.log(mergeAll(a,b,c).map(function(x){return x.name;}));
    

    jsfiddle: http://jsfiddle.net/FeT6m/

    0 讨论(0)
  • 2020-12-17 17:18

    The native implementations are not always the fastest (as you may have noticed) and have, historically, been somewhat sluggish, due to extensive error checking. That being said, there may be performance enhancements in the future, due to more robust integration with the hardware or routines specifically built to optimize certain tasks. If you write your own code, your application won't be able to take advantage of these boosts in performance once they're implemented. It's up to you to decide where the advantages lie and what the risks are.

    At any rate, I've written a prettier version of your optimized code for funsies:

    function mergeSorted(a,b){
        var alen = a.length
          , blen = b.length
          , i, j, k = j = i = 0
          , answer = new Array(alen + blen)
        ;//var
    
        while(i < alen && j < blen)
                        answer[k++] = a[i].name < b[j].name ? a[i++] : b[j++];
        while(i < alen) answer[k++] = a[i++];
        while(j < blen) answer[k++] = b[j++];
    
        return answer;
    }
    
    0 讨论(0)
  • 2020-12-17 17:20

    Faster, merges in only 1 pass, with more flexibility (keepDuplicates, custom comparator):

    /*  mergeSortedArrays(arrays[, keepDuplicates[, comparator[, thisArg]]])
        Merges multiple sorted arrays into a new sorted array.
        Arguments:
            - arrays: array of sorted arrays to be merged
            - keepDuplicates (optional): (true/false) whether to keep duplicate values
                Default: false
            - comparator (optional): function used to compare values
                Default: sort numbers in ascending order
                Example comparator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
            - thisArg (optional): comparator is bound to thisArg when invoked
        Returns: a new sorted array containing all the values from the arrays
    */
    function mergeSortedArrays(arrays, keepDuplicates, comparator, thisArg) {
        // Coerce to boolean to speed up testings in some javascript engines:
        keepDuplicates = !!keepDuplicates;
    
        // By default, sort numbers in ascending order:
        if(!comparator) comparator = function(a, b) { return a - b; };
    
        var nb = arrays.length,     // Number of arrays to be merged
            iter = new Array(nb),   // Current position of iteration of each array
            next = [],              // Keep each array sorted by the value of their next element
            length = 0;             // The combined length of all arrays
    
        // Populate iter and next:
        for(var i = 0, arr; i < nb; i++) {
            arr = arrays[i];
            iter[i] = 0;
            if(arr.length > 0) {
                insertNextIndex(next, i, arr[0], comparator, thisArg);
            }
            length += arr.length;
        }
    
        // Insert index of array into next:
        function insertNextIndex(next, index, val, comparator, thisArg) {
            var i = next.length;
            while(i--) {    // Reverse loop...
                var j = next[i];
                if(comparator.call(thisArg, arrays[j][iter[j]], val) >= 0) {    // ...until we find a greater value
                    break;
                }
            }
            next.splice(i + 1, 0, index);
        }
    
    
        var merged = keepDuplicates ? new Array(length) : [],
            k = 0,  // Iterate over merged
            min, val, lastVal;
    
        // First iteration to get a value for lastVal (for duplicate checks):
        if(!keepDuplicates && next.length > 0) {
            min = next.pop();
            arr = arrays[min];
            i = iter[min]++;
            val = arr[i];
            merged[k++] = val;
            lastVal = val;
            if(++i < arr.length) {  // If available, insert next value in next:
                insertNextIndex(next, min, arr[i], comparator, thisArg);
            }
        }
    
        // Merge multiple arrays:
        while(next.length > 1) {    // While there is still multiple arrays to be merged
            min = next.pop();
            arr = arrays[min];
            i = iter[min]++;
            val = arr[i];
            if(keepDuplicates || comparator.call(thisArg, lastVal, val) !== 0) {
                merged[k++] = val;
                lastVal = val;
            }
            if(++i < arr.length) {  // If available, insert next value in next:
                insertNextIndex(next, min, arr[i], comparator, thisArg);
            }
        }
    
        // When there remain only 1 array with unmerged values, use a faster loop:
        if(next.length > 0) {
            arr = arrays[next[0]];
            i = iter[next[0]];
            length = arr.length;
    
            while(i < length) { // To the end
                val = arr[i++];
                if(keepDuplicates || comparator.call(thisArg, lastVal, val) !== 0) {
                    merged[k++] = val;
                    lastVal = val;
                }
            }
        }
    
        return merged;
    }
    

    Merging in 1 pass eliminates the creation of intermediate arrays which takes time and memory. Also, the number of comparisons is nicely reduced by keeping a sorted list of the next element from each array (see the next array). And when array sizes are known, they are pre-allocated to prevent dynamic re-allocations (though that will depend on your javascript engine).

    For your case, I would call it like this:

    mergeSortedArrays(arrays, true, function(a, b) {
        return a.name < b.name ? -1 : 1;
    });
    

    Note: If you have a large number of arrays you may benefit from using a binary search instead of the linear search in insertNextIndex(). Or from using a Binary Heap for next.

    0 讨论(0)
  • 2020-12-17 17:26

    The standard and most understanding code I believe..

    function mergeArray(arr1, arr2) {
     var new_array = [];
     var i = 0,
         j = 0,
         index = 0;
    
     while (new_array.length != (arr1.length + arr2.length) - 1) {
         if (arr1[i] < arr2[j]) {
             new_array.push(arr1[i]);
             i++;
         } else {
             new_array.push(arr2[j]);
             j++;
         }
     }
     return new_array;
    }
    

    Function call:

    var merged_array = mergeArray([1,6,9,95], [2,7,10,11,14,18]);
    
    0 讨论(0)
提交回复
热议问题