9

There's some similar posts knocking about but I cant find anything that quite addresses this particular issue... I have two arrays of paired values:

var A=[0.5, 0.6, 0.5, 0.7, 0.8, 0.1]
var B=['a','b','c','d','e','f']
//note: a=0.5, b=0.6, c=0.5, d=0.7, etc

What's the most processor friendly way to sort the arrays so that array A is in numerical ascending order and the data structure is maintained? I guess built in array.sort(function) would be quickest but I'm not confident of the syntax.

alex
  • 479,566
  • 201
  • 878
  • 984
cronoklee
  • 6,482
  • 9
  • 52
  • 80

7 Answers7

12

Kind of hacky, but it works.

var A = [0.5, 0.6, 0.5, 0.7, 0.8, 0.1];
var B = ['a', 'b', 'c', 'd', 'e', 'f'];

var all = [];

for (var i = 0; i < B.length; i++) {
    all.push({ 'A': A[i], 'B': B[i] });
}

all.sort(function(a, b) {
  return a.A - b.A;
});

A = [];
B = [];

for (var i = 0; i < all.length; i++) {
   A.push(all[i].A);
   B.push(all[i].B);
}    
    
console.log(A, B);

jsFiddle.

Output

0.1, 0.5, 0.5, 0.6, 0.7, 0.8
["f", "a", "c", "b", "d", "e"]

Basically, we are making objects with a clear tie between A and B within a new array, and then sort()ing that.

Then I go back and rebuild the original the two arrays.

Update

Már Örlygsson makes a good point in the comments. Instead of producing an object like {A: 0.5, B: 'a'}, he suggests placing the A an B values in arrays like [0.5, 'a'].

This should be faster, though it will be slightly less readable if needing to debug the all array. I'll leave this up to you, if you are experiencing performance issues, profile both these methods and choose the fastest.

Community
  • 1
  • 1
alex
  • 479,566
  • 201
  • 878
  • 984
  • Isn't that output just the same as sorting one of the arrays? I don't see the use for the other one in your answer. – Alxandr Mar 25 '11 at 00:45
  • Scratch that, I ran your code on jsFiddle, and the other array get's sorted too. Though you should include that in your `output` section. – Alxandr Mar 25 '11 at 00:46
  • 1
    populating `all` with `all.push( [ A[i], B[i] ] )` and then sorting with `a[0] - b[0]` will probably be quite a bit faster, and use less memory, especially when `A` and `B` grow larger. – Már Örlygsson Mar 25 '11 at 00:47
  • @Már I agree with you, but I shall leave my answer *as is* and provide a note :) – alex Mar 25 '11 at 00:48
  • Here's an updated jsFiddle test using arrays and fewer function calls: http://jsfiddle.net/Bnp8t/1/ – Már Örlygsson Mar 25 '11 at 01:09
9

I noticed all the above solutions used a map. Another method that saves a bit of memory is to create an array of indices, sort the indices based on A, and then reconstruct A and B based on the indices.

var A=[0.5, 0.6, 0.5, 0.7, 0.8, 0.1];
var B=['a','b','c','d','e','f'];
var indices = A.map(function(elem, index){return index;}); //an array of indices
indices.sort(function (a,b) {return A[a] - A[b];});

//print out the results
for (var i = 0; i < A.length; i++)
    document.body.innerHTML += 'A: ' + A[indices[i]] + ', B: ' + B[indices[i]] + '<br>';

here's the jsfiddle

EDIT: semi-one-liner inspired by chowey's comment:

var BSorted = A.map(function(e,i){return i;})
               .sort(function(a,b){return A[a] - A[b];})
               .map(function(e){return B[e];});
Community
  • 1
  • 1
woojoo666
  • 7,801
  • 7
  • 45
  • 57
  • 1
    Can reassemble using map: `A = A.map(function(v,i,A){return A[indices[i]]})`. – chowey Feb 17 '15 at 08:08
  • 1
    it's actually a tad simpler to apply map to the indices `indices.map(function(i){return B[i];});`. Using this, you can actually chain the entire thing to `var BSorted = A.map(function(e,i){return i;}).sort(function(a,b){return A[a] - A[b];}).map(function(e){return B[e];});` – woojoo666 Feb 18 '15 at 00:51
4

It would be much simpler if you had one array with tuples instead of two arrays. Because then you can use the build-in Array.sort().

var arr = [{a: 0.5, b: 'a'}, {a: 0.6, b: 'b'}, {a: 0.5, b: 'c'}, ... ];

After this you can just write:

arr.sort(function(x,y) { return x.a - y.a });
Elian Ebbing
  • 18,779
  • 5
  • 48
  • 56
  • 1
    You can also make an array of tuples from two arrays using a "zip" function -- easy to implement and works in O(n). – Zach Mar 25 '11 at 00:40
  • 1
    populating the `arr` Array with two item arrays (`[ A[i], B[i] ]`) instead of objects will probably be both faster and more memory-efficient. – Már Örlygsson Mar 25 '11 at 00:50
1
Array.prototype.swap=function(a, b)
{
    var tmp=this[a];
    this[a]=this[b];
    this[b]=tmp;
}

function partition(array, array2, begin, end, pivot)
{
    var piv=array[pivot];
    array.swap(pivot, end-1);
    array2.swap(pivot, end-1);
    var store=begin;
    var ix;
    for(ix=begin; ix<end-1; ++ix) {
        if(array[ix]<=piv) {
            array.swap(store, ix);
            array2.swap(store, ix);
            ++store;
        }
    }
    array.swap(end-1, store);
    array2.swap(end-1, store);

    return store;
}

function qsort(array, array2, begin, end)
{
    if(end-1>begin) {
        var pivot=begin+Math.floor(Math.random()*(end-begin));

        pivot=partition(array, array2, begin, end, pivot);

        qsort(array, array2, begin, pivot);
        qsort(array, array2, pivot+1, end);
    }
}

array is for numbers, array2 is for strings, they must be of same length.

this is quicksort so time is O(N LogN)

source is literateprograms.org, license is MIT, modification to manage the second array made by me

Marino Šimić
  • 7,318
  • 1
  • 31
  • 61
  • 2
    I wonder how well a pure javascript quicksort implementation performs compared to the native build-in version (which I believe is a mergesort implementation). – Elian Ebbing Mar 25 '11 at 00:36
  • Actually, ECMA spec states that the sort algorithm is "implementation defined", so it's up to the javascript engine design. That said, I would have a hard time believing that a javascript sort algorithm has the capability of performing faster than the any existing native implementation. – jordancpaul Mar 25 '11 at 00:50
  • 2
    I appreciate your use of google, but if you are going to post a [solution](http://en.literateprograms.org/Quicksort_(JavaScript)) you should give credit to the original source. Also, your changes make passing in begin and end to the recursive calls unnecessary – jordancpaul Mar 25 '11 at 01:01
  • @jordancpaul I agree, i could have given credit it would be polite, but I was in a hurry whn posting this. I have updated the post. – Marino Šimić Mar 25 '11 at 02:15
0

With any sorting algorithm, there is a tradeoff between algorithm time, and other constraints (usually memory). You might want to take a look at Quicksort and Bubble Sort, two common and very fast sorting algorithms. You would need to make minor modifications, since you are actually sorting two array's with the first being the source of the data to sort - but this sort of change is trivial.

EDIT: Note, array.sort() would not work for you since you want changes in arr1 to be reflected in arr2. You have to write your own sorting function

jordancpaul
  • 2,954
  • 1
  • 18
  • 27
0

Putting the two arrays together before the sort is the simplest way.

var a1, b1, A= [0.5, 0.6, 0.5, 0.7, 0.8, 0.1],
B= ['a', 'b', 'c', 'd', 'e', 'f'],    
ab= A.map(function(itm, i){ return [itm, i]; });

ab.sort(function(a, b){ return a[0]-b[0]; });

B= ab.map(function(itm){
    A.push(itm[0]);
    return B[itm[1]];
});
A= A.splice(B.length);

alert(A+'\n'+B)

/*  returned value: (String)
0.1, 0.5, 0.5, 0.6, 0.7, 0.8
f, a, c, b, d, e
*/
kennebec
  • 102,654
  • 32
  • 106
  • 127
-1

You might be better off, if possible, actually making a data structure. This way you can sort $A[] based on one property while maintaining the structure. I havent done any speed tests or anything, but maybe something like...

A[0]['label']='a';
A[0]['value']=0.5;

A[1]['label']='b';
A[1]['value']=0.6;

A[2]['label']='c';
A[2]['value']=0.5;

A[3]['label']='d';
A[3]['value']=0.7;

A[4]['label']='e';
A[4]['value']=0.8;

A[5]['label']='f';
A[5]['value']=0.1;
Dutchie432
  • 28,798
  • 20
  • 92
  • 109