What is the javascript equivalent of numpy argsort?

Viewed 3236

I want to sort the imgUrl array by click count. I have two arrays.

clickCount = [5,2,4,3,1]
imgUrl     = ['1.jpg','2.jpg','3.jpg','4.jpg','5.jpg']

In numpy it is easy. I use order = np.argsort(clickCount) then I create another array newArray = [imgUrl[i] for i in order].

How do I achieve the same effect in javascript (preferably vanilla)?

3 Answers

You can use a Schwartzian transform also known as Decorate-Sort-Undecorate (DSU) in python.

DSU:

  1. Decorate - Use Array#Map to enrich each item in the array with the needed sort data
  2. Sort - sort using the added data
  3. Undecorate - extract the sorted data using Array#map again

Demo:

const dsu = (arr1, arr2) => arr1
  .map((item, index) => [arr2[index], item]) // add the args to sort by
  .sort(([arg1], [arg2]) => arg2 - arg1) // sort by the args
  .map(([, item]) => item); // extract the sorted items

const clickCount = [5,2,4,3,1];
const imgUrl = ['1.jpg','2.jpg','3.jpg','4.jpg','5.jpg'];

const result = dsu(imgUrl, clickCount);
  
console.log(result);

thanks to dankal444 for the refactor to the function

For completeness, here's my solution to the actual answer (providing argsort function), by expanding on Ori's answer with DSU. Since sort is by default taking the first element, so implementing it as DSU is merely adding an index, sorting it, then taking the indices.

let decor = (v, i) => [v, i];          // set index to value
let undecor = a => a[1];               // leave only index
let argsort = arr => arr.map(decor).sort().map(undecor);

clickCount = [5, 2, 4, 3, 1]
imgUrl = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg']

order = argsort(clickCount);
newArray = order.map(i => imgUrl[i])

console.log(newArray);

Functional approach (like @Ori Drori's code) is always a charm to watch, but in this case, you only need to re-arrange an array's items. I believe that there is a simpler way to go and is a much easier code to read.

const clickCount = [5,2,4,3,1];
const imgUrl = ['1.jpg','2.jpg','3.jpg','4.jpg','5.jpg'];

sortByArrayRefOrder = (data, orderRefArr) => {
 let orderedArr = [], i=0;
 orderRefArr.map( o => { orderedArr[o-1] = data[i++]});
 return orderedArr.reverse();
}

console.log ( sortByArrayRefOrder(imgUrl, clickCount) );

Related