Implementación rápida y estable del algoritmo de clasificación en javascript

104

Estoy buscando ordenar una matriz de aproximadamente 200-300 objetos, ordenando una clave específica y un orden dado (asc / desc). El orden de los resultados debe ser consistente y estable.

¿Cuál sería el mejor algoritmo para usar y podría proporcionar un ejemplo de su implementación en javascript?

¡Gracias!

William Casarin
fuente
6
Dado que al menos la clasificación de la matriz de Chrome no parece ser estable, confiar en la clasificación de la matriz incorporada no es una opción para usted.
Nosredna
Para resumir: elegí una clasificación de fusión enrollada a mano debido a las inconsistencias de estabilidad de Array.sort entre los navegadores modernos (principalmente Chrome no implementa una clasificación estable en el momento de este comentario). ¡Gracias a todos por su ayuda!
William Casarin
¿Qué entendemos por tipo "estable"?
mowwwalker
2
@mowwwalker La clasificación estable es una clasificación en la que todos los elementos con el mismo valor de clasificación se dejan en el mismo orden que en la colección original. en.wikipedia.org/wiki/Sorting_algorithm#Stability
Kornelije Petak
Para responder "cuál es el mejor algoritmo para usar", necesitamos saber si existe alguna estructura subyacente a sus datos. Muchas de las respuestas a continuación solo hablan sobre el uso de la clasificación por combinación, o clasificación rápida, en realidad depende de los datos. No es un problema simple responder, no diría. Busca en Google algunos algoritmos de clasificación y lee sobre ellos para ver a qué me refiero. TimSort y Radix Sort son dos buenos ejemplos sobre los que recomendaría leer.
será el

Respuestas:

114

Es posible obtener una clasificación estable a partir de una función de clasificación no estable.

Antes de ordenar obtienes la posición de todos los elementos. En su condición de ordenación, si ambos elementos son iguales, ordena por posición.

Tada! Tienes un tipo estable.

He escrito un artículo al respecto en mi blog si quieres saber más sobre esta técnica y cómo implementarla: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html

Vjeux
fuente
32
¿Podría proporcionar la respuesta aquí en lugar de publicar y vincular a su blog? Gracias
Mohammad Kermani
4
Un enlace a una solución es bienvenido, pero asegúrese de que su respuesta sea útil sin él: agregue contexto alrededor del enlace para que sus compañeros usuarios tengan una idea de qué es y por qué está allí, luego cite la parte más relevante de la página. volver a enlazar en caso de que la página de destino no esté disponible. Las respuestas que son poco más que un enlace pueden eliminarse.
Samuel Liew
@Vjeux ya que esto se está volviendo popular, ¿te importaría pegar el código relevante en esta respuesta? ¡Sería de gran ayuda! ¡Gracias!
William Casarin
34

Dado que está buscando algo estable, la ordenación por combinación debería funcionar.

http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/

El código se puede encontrar en el sitio web anterior:

function mergeSort(arr)
{
    if (arr.length < 2)
        return arr;

    var middle = parseInt(arr.length / 2);
    var left   = arr.slice(0, middle);
    var right  = arr.slice(middle, arr.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
    var result = [];

    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}

EDITAR:

Según esta publicación , parece que Array.Sort en algunas implementaciones usa una ordenación por combinación.

kemiller2002
fuente
++ El tipo de combinación es mi favorito. Es simple y estable sin los peores casos.
Mike Dunlavey
El enlace al sitio web no está disponible :(
ahitt6345
encontró un nuevo sitio web para el ejemplo.
kemiller2002
7
Nota: Array#shiftpuede funcionar en O (n) tiempo y, por lo tanto, está mergeen O (n * n).
4esn0k
22

Versión algo más corta de lo mismo con funciones de ES2017 como funciones de flecha y desestructuración:

Función

var stableSort = (arr, compare) => arr
  .map((item, index) => ({item, index}))
  .sort((a, b) => compare(a.item, b.item) || a.index - b.index)
  .map(({item}) => item)

Acepta matriz de entrada y función de comparación:

stableSort([5,6,3,2,1], (a, b) => a - b)

También devuelve una nueva matriz en lugar de realizar una ordenación en el lugar como el Array.sort () incorporado .

Prueba

Si tomamos la siguiente inputmatriz, inicialmente ordenada por weight:

// sorted by weight
var input = [
  { height: 100, weight: 80 },
  { height: 90, weight: 90 },
  { height: 70, weight: 95 },
  { height: 100, weight: 100 },
  { height: 80, weight: 110 },
  { height: 110, weight: 115 },
  { height: 100, weight: 120 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 100, weight: 135 },
  { height: 75, weight: 140 },
  { height: 70, weight: 140 }
]

Luego ordénelo heightusando stableSort:

stableSort(input, (a, b) => a.height - b.height)

Resultados en:

// Items with the same height are still sorted by weight 
// which means they preserved their relative order.
var stable = [
  { height: 70, weight: 95 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 70, weight: 140 },
  { height: 75, weight: 140 },
  { height: 80, weight: 110 },
  { height: 90, weight: 90 },
  { height: 100, weight: 80 },
  { height: 100, weight: 100 },
  { height: 100, weight: 120 },
  { height: 100, weight: 135 },
  { height: 110, weight: 115 }
]

Sin embargo, ordenando la misma inputmatriz usando el incorporado Array.sort()(en Chrome / NodeJS):

input.sort((a, b) => a.height - b.height)

Devoluciones:

var unstable = [
  { height: 70, weight: 140 },
  { height: 70, weight: 95 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 75, weight: 140 },
  { height: 80, weight: 110 },
  { height: 90, weight: 90 },
  { height: 100, weight: 100 },
  { height: 100, weight: 80 },
  { height: 100, weight: 135 },
  { height: 100, weight: 120 },
  { height: 110, weight: 115 }
]

Recursos

Actualizar

Array.prototype.sort ahora es estable en V8 v7.0 / Chrome 70!

Anteriormente, V8 usaba un QuickSort inestable para arreglos con más de 10 elementos. Ahora, usamos el algoritmo estable TimSort.

fuente

simo
fuente
1
¡La stableSortfunción es una gran solución!
lhermann
16

Sé que esta pregunta ha sido respondida por algún tiempo, pero resulta que tengo una buena implementación estable de ordenación de fusión para Array y jQuery en mi portapapeles, así que la compartiré con la esperanza de que algunos futuros buscadores la encuentren útil.

Le permite especificar su propia función de comparación como la normal Array.sort implementación .

Implementación

// Add stable merge sort to Array and jQuery prototypes
// Note: We wrap it in a closure so it doesn't pollute the global
//       namespace, but we don't put it in $(document).ready, since it's
//       not dependent on the DOM
(function() {

  // expose to Array and jQuery
  Array.prototype.mergeSort = jQuery.fn.mergeSort = mergeSort;

  function mergeSort(compare) {

    var length = this.length,
        middle = Math.floor(length / 2);

    if (!compare) {
      compare = function(left, right) {
        if (left < right)
          return -1;
        if (left == right)
          return 0;
        else
          return 1;
      };
    }

    if (length < 2)
      return this;

    return merge(
      this.slice(0, middle).mergeSort(compare),
      this.slice(middle, length).mergeSort(compare),
      compare
    );
  }

  function merge(left, right, compare) {

    var result = [];

    while (left.length > 0 || right.length > 0) {
      if (left.length > 0 && right.length > 0) {
        if (compare(left[0], right[0]) <= 0) {
          result.push(left[0]);
          left = left.slice(1);
        }
        else {
          result.push(right[0]);
          right = right.slice(1);
        }
      }
      else if (left.length > 0) {
        result.push(left[0]);
        left = left.slice(1);
      }
      else if (right.length > 0) {
        result.push(right[0]);
        right = right.slice(1);
      }
    }
    return result;
  }
})();

Ejemplo de uso

var sorted = [
  'Finger',
  'Sandwich',
  'sandwich',
  '5 pork rinds',
  'a guy named Steve',
  'some noodles',
  'mops and brooms',
  'Potato Chip Brand® chips'
].mergeSort(function(left, right) {
  lval = left.toLowerCase();
  rval = right.toLowerCase();

  console.log(lval, rval);
  if (lval < rval)
    return -1;
  else if (lval == rval)
    return 0;
  else
    return 1;
});

sorted == ["5 pork rinds", "a guy named Steve", "Finger", "mops and brooms", "Potato Chip Brand® chips", "Sandwich", "sandwich", "some noodles"];
Justin Force
fuente
4
Tenga en cuenta que esto está en desacuerdo con el tipo nativo, que funciona en su lugar, lo que significa que no puede simplemente introducirse.
Eric
3
Quizás estable, pero este método es aproximadamente 20 veces más lento que el nativo array.sort, consulte la prueba aquí para cadenas y enteros -> jsfiddle.net/QC64j
davidkonrad
2
Por supuesto, es más lento que el tipo nativo, no es nativo. Eso es imposible. También es cierto que no hace una clasificación en el lugar. Eso también es imposible (en el mejor de los casos, crea una copia y luego sobrescribe el original). Además, devolver una copia ordenada es más JavaScript a pesar del comportamiento de ordenación nativo de JavaScript. La función también se llama mergeSorty no sort, por lo que no pretende ser un reemplazo directo. A veces, solo necesita una clasificación de fusión estable, por ejemplo, al ordenar tablas por columna.
Justin Force
1
Incorrecto, el tipo nativo de Node está escrito en javascript. Es completamente posible que un algoritmo programado en javascript supere en velocidad al tipo nativo. Construí un algoritmo de clasificación completamente en javascript (un tipo de clasificación de fusión adaptativa) que Kremes / creams / Kreams The native quicksort in node. El objetivo de este comentario es mostrar que nativo no importa en el caso de javascript porque el algoritmo de clasificación está escrito en javascript y no en algún lenguaje superior como c ++. La prueba está aquí: github.com/nodejs/node/blob/master/deps/v8/src/js/array.js
ahitt6345
2
Para cualquiera que quiera una solución inmediata que sea mucho más rápida que esta implementación, consulte mi respuesta .
Patrick Roberts
10

Puede usar el siguiente polyfill para implementar una ordenación estable independientemente de la implementación nativa, según la afirmación realizada en esta respuesta :

// ECMAScript 5 polyfill
Object.defineProperty(Array.prototype, 'stableSort', {
  configurable: true,
  writable: true,
  value: function stableSort (compareFunction) {
    'use strict'

    var length = this.length
    var entries = Array(length)
    var index

    // wrap values with initial indices
    for (index = 0; index < length; index++) {
      entries[index] = [index, this[index]]
    }

    // sort with fallback based on initial indices
    entries.sort(function (a, b) {
      var comparison = Number(this(a[1], b[1]))
      return comparison || a[0] - b[0]
    }.bind(compareFunction))

    // re-map original array to stable sorted values
    for (index = 0; index < length; index++) {
      this[index] = entries[index][1]
    }
    
    return this
  }
})

// usage
const array = Array(500000).fill().map(() => Number(Math.random().toFixed(4)))

const alwaysEqual = () => 0
const isUnmoved = (value, index) => value === array[index]

// not guaranteed to be stable
console.log('sort() stable?', array
  .slice()
  .sort(alwaysEqual)
  .every(isUnmoved)
)
// guaranteed to be stable
console.log('stableSort() stable?', array
  .slice()
  .stableSort(alwaysEqual)
  .every(isUnmoved)
)

// performance using realistic scenario with unsorted big data
function time(arrayCopy, algorithm, compare) {
  var start
  var stop
  
  start = performance.now()
  algorithm.call(arrayCopy, compare)
  stop = performance.now()
  
  return stop - start
}

const ascending = (a, b) => a - b

const msSort = time(array.slice(), Array.prototype.sort, ascending)
const msStableSort = time(array.slice(), Array.prototype.stableSort, ascending)

console.log('sort()', msSort.toFixed(3), 'ms')
console.log('stableSort()', msStableSort.toFixed(3), 'ms')
console.log('sort() / stableSort()', (100 * msSort / msStableSort).toFixed(3) + '%')

Al ejecutar las pruebas de rendimiento implementadas anteriormente, stableSort()parece ejecutarse a aproximadamente el 57% de la velocidad desort() versión 59-61 de Chrome.

El uso .bind(compareFunction)de la función anónima envuelta dentro stableSort()aumentó ese rendimiento relativo de aproximadamente un 38% al evitar una referencia de ámbito innecesaria acompareFunction en cada llamada asignándola al contexto en su lugar.

Actualizar

Se cambió el operador ternario a un cortocircuito lógico, que tiende a funcionar mejor en promedio (parece tener una diferencia del 2-3% en la eficiencia).

Patrick Roberts
fuente
5

Lo siguiente ordena la matriz proporcionada, aplicando la función de comparación proporcionada, devolviendo la comparación de índice original cuando la función de comparación devuelve 0:

function stableSort(arr, compare) {
    var original = arr.slice(0);

    arr.sort(function(a, b){
        var result = compare(a, b);
        return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
    });

    return arr;
}

El siguiente ejemplo ordena una matriz de nombres por apellido, conservando el orden de apellidos iguales:

var names = [
	{ surname: "Williams", firstname: "Mary" },
	{ surname: "Doe", firstname: "Mary" }, 
	{ surname: "Johnson", firstname: "Alan" }, 
	{ surname: "Doe", firstname: "John" }, 
	{ surname: "White", firstname: "John" }, 
	{ surname: "Doe", firstname: "Sam" }
]

function stableSort(arr, compare) {
    var original = arr.slice(0);

    arr.sort(function(a, b){
        var result = compare(a, b);
        return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
    });
	
    return arr;
}

stableSort(names, function(a, b) { 
	return a.surname > b.surname ? 1 : a.surname < b.surname ? -1 : 0;
})

names.forEach(function(name) {
	console.log(name.surname + ', ' + name.firstname);
});

Philip Bijker
fuente
No estable para tipos primitivos o elementos duplicados en una matriz. jQuery.expando.split( "" ).sort( ( a, b ) => 0 ).join( "" ) === jQuery.expando
marciano
3

Aquí hay una implementación estable. Funciona utilizando la ordenación nativa, pero en los casos en que los elementos se comparan como iguales, se rompen los vínculos utilizando la posición del índice original.

function stableSort(arr, cmpFunc) {
    //wrap the arr elements in wrapper objects, so we can associate them with their origional starting index position
    var arrOfWrapper = arr.map(function(elem, idx){
        return {elem: elem, idx: idx};
    });

    //sort the wrappers, breaking sorting ties by using their elements orig index position
    arrOfWrapper.sort(function(wrapperA, wrapperB){
        var cmpDiff = cmpFunc(wrapperA.elem, wrapperB.elem);
        return cmpDiff === 0 
             ? wrapperA.idx - wrapperB.idx
             : cmpDiff;
    });

    //unwrap and return the elements
    return arrOfWrapper.map(function(wrapper){
        return wrapper.elem;
    });
}

una prueba no exhaustiva

var res = stableSort([{a:1, b:4}, {a:1, b:5}], function(a, b){
    return a.a - b.a;
});
console.log(res);

otra respuesta aludió a esto, pero no publicó el código.

pero no es rápido según mi punto de referencia . Modifiqué un impl de clasificación de combinación para aceptar una función de comparador personalizada, y fue mucho más rápido.

cabra
fuente
¿Tu punto de referencia es correcto? Parece que su "stableSort" no modifica la matriz de entrada, otros tipos, sí, y como no volvió a crear "arr" durante la "configuración", otros tipos clasifican las matrices ya ordenadas ....
4esn0k
@ 4esn0k ¿leíste mal? Dije que mi función stableSort NO era rápida.
cabra
@gota, ah, perdóname
4esn0k
3

También puede utilizar Timsort. Este es un algoritmo realmente complicado (más de 400 líneas, por lo tanto, no hay código fuente aquí), así que consulte la descripción de Wikipedia o use una de las implementaciones de JavaScript existentes:

Implementación de GPL 3 . Empaquetado como Array.prototype.timsort. Parece ser una reescritura exacta del código Java.

Implementación de dominio público Concebido como un tutorial, el código de muestra solo muestra su uso con números enteros.

Timsort es un híbrido altamente optimizado de mergesort y shuffle sort y es el algoritmo de ordenación predeterminado en Python y en Java (1.7+). Es un algoritmo complicado, ya que utiliza diferentes algoritmos para muchos casos especiales. Pero, como resultado, es extremadamente rápido en una amplia variedad de circunstancias.

David Leppik
fuente
1

Un simple mergeSort de http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/

var a = [34, 203, 3, 746, 200, 984, 198, 764, 9];

function mergeSort(arr)
{
    if (arr.length < 2)
         return arr;

    var middle = parseInt(arr.length / 2);
    var left   = arr.slice(0, middle);
    var right  = arr.slice(middle, arr.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
     var result = [];

    while (left.length && right.length) {
         if (left[0] <= right[0]) {
             result.push(left.shift());
         } else {
            result.push(right.shift());
         }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}

console.log(mergeSort(a));
Demóstenes
fuente
0

Tengo que ordenar matrices multidimensionales por una columna arbitraria y luego por otra. Utilizo esta función para ordenar:

function sortMDArrayByColumn(ary, sortColumn){

    //Adds a sequential number to each row of the array
    //This is the part that adds stability to the sort
    for(var x=0; x<ary.length; x++){ary[x].index = x;}

    ary.sort(function(a,b){
        if(a[sortColumn]>b[sortColumn]){return 1;}
        if(a[sortColumn]<b[sortColumn]){return -1;}
        if(a.index>b.index){
            return 1;
        }
        return -1;
    });
}

Observe que ary.sort nunca devuelve cero, que es donde algunas implementaciones de la función "sort" toman decisiones que pueden no ser correctas.

Esto también es bastante rápido.

alfadog67
fuente
0

A continuación, le mostramos cómo puede extender el objeto Array predeterminado de JS con un método prototipo utilizando MERGE SORT . Este método permite ordenar por una clave específica (primer parámetro) y un orden dado ('asc' / 'desc' como segundo parámetro)

Array.prototype.mergeSort = function(sortKey, direction){
  var unsortedArray = this;
  if(unsortedArray.length < 2) return unsortedArray;

  var middle = Math.floor(unsortedArray.length/2);
  var leftSubArray = unsortedArray.slice(0,middle).mergeSort(sortKey, direction);
  var rightSubArray = unsortedArray.slice(middle).mergeSort(sortKey, direction);

  var sortedArray = merge(leftSubArray, rightSubArray);
  return sortedArray;

  function merge(left, right) {
    var combined = [];
    while(left.length>0 && right.length>0){
      var leftValue = (sortKey ? left[0][sortKey] : left[0]);
      var rightValue = (sortKey ? right[0][sortKey] : right[0]);
      combined.push((direction === 'desc' ? leftValue > rightValue : leftValue < rightValue) ? left.shift() : right.shift())
    }
    return combined.concat(left.length ? left : right)
  }
}

Puede probar esto usted mismo colocando el fragmento anterior en la consola de su navegador y luego intentando:

var x = [2,76,23,545,67,-9,12];
x.mergeSort(); //[-9, 2, 12, 23, 67, 76, 545]
x.mergeSort(undefined, 'desc'); //[545, 76, 67, 23, 12, 2, -9]

O haga un pedido basado en un campo específico en una matriz de objetos:

var y = [
  {startTime: 100, value: 'cat'},
  {startTime: 5, value: 'dog'},
  {startTime: 23, value: 'fish'},
  {startTime: 288, value: 'pikachu'}
]
y.mergeSort('startTime');
y.mergeSort('startTime', 'desc');
Cumulo Nimbus
fuente
0

Así que necesitaba un tipo estable para mi aplicación React + Redux, y la respuesta de Vjeux aquí me ayudó. Sin embargo, mi solución (genérica) parece diferente a las otras que veo aquí hasta ahora, así que la comparto en caso de que alguien más tenga un caso de uso coincidente:

  • Realmente solo quiero tener algo similar a la sort()API, donde puedo pasar una función de comparación.
  • A veces puedo ordenar en el lugar y, a veces, mis datos son inmutables (debido a Redux) y necesito una copia ordenada en su lugar. Entonces necesito una función de clasificación estable para cada caso de uso.
  • ES2015.

Mi solución es crear una matriz con tipo de indicesy luego usar una función de comparación para ordenar estos índices en función de la matriz que se va a ordenar. Luego, podemos usar sorted indicespara ordenar la matriz original o crear una copia ordenada en una sola pasada. Si eso es confuso, piénselo de esta manera: donde normalmente pasaría una función de comparación como:

(a, b) => { 
  /* some way to compare a and b, returning -1, 0, or 1 */ 
};

En su lugar, usa ahora:

(i, j) => { 
  let a = arrayToBeSorted[i], b = arrayToBeSorted[j]; 
  /* some way to compare a and b, returning -1 or 1 */
  return i - j; // fallback when a == b
}

La velocidad es buena; es básicamente el algoritmo de clasificación integrado, más dos pasadas lineales al final, y una capa adicional de sobrecarga de indirección del puntero.

Feliz de recibir comentarios sobre este enfoque. Aquí está mi implementación completa:

/**
 * - `array`: array to be sorted
 * - `comparator`: closure that expects indices `i` and `j`, and then
 *   compares `array[i]` to `array[j]` in some way. To force stability,
 *   end with `i - j` as the last "comparison".
 * 
 * Example:
 * ```
 *  let array = [{n: 1, s: "b"}, {n: 1, s: "a"}, {n:0, s: "a"}];
 *  const comparator = (i, j) => {
 *    const ni = array[i].n, nj = array[j].n;
 *    return ni < nj ? -1 :
 *      ni > nj ? 1 :
 *        i - j;
 *  };
 *  stableSortInPlace(array, comparator);
 *  // ==> [{n:0, s: "a"}, {n:1, s: "b"}, {n:1, s: "a"}]
 * ```
 */
function stableSortInPlace(array, comparator) {
  return sortFromIndices(array, findIndices(array, comparator));
}

function stableSortedCopy(array, comparator){
  let indices = findIndices(array, comparator);
  let sortedArray = [];
  for (let i = 0; i < array.length; i++){
    sortedArray.push(array[indices[i]]);
  }
  return sortedArray;
}

function findIndices(array, comparator){
  // Assumes we don't have to worry about sorting more than 
  // 4 billion elements; if you know the upper bounds of your
  // input you could replace it with a smaller typed array
  let indices = new Uint32Array(array.length);
  for (let i = 0; i < indices.length; i++) {
    indices[i] = i;
  }
  // after sorting, `indices[i]` gives the index from where
  // `array[i]` should take the value from, so to sort
  // move the value at at `array[indices[i]]` to `array[i]`
  return indices.sort(comparator);
}

// If I'm not mistaken this is O(2n) - each value is moved
// only once (not counting the vacancy temporaries), and 
// we also walk through the whole array once more to check
// for each cycle.
function sortFromIndices(array, indices) {
  // there might be multiple cycles, so we must
  // walk through the whole array to check.
  for (let k = 0; k < array.length; k++) {
    // advance until we find a value in
    // the "wrong" position
    if (k !== indices[k]) {
      // create vacancy to use "half-swaps" trick,
      // props to Andrei Alexandrescu
      let v0 = array[k];
      let i = k;
      let j = indices[k];
      while (j !== k) {
        // half-swap next value
        array[i] = array[j];
        // array[i] now contains the value it should have,
        // so we update indices[i] to reflect this
        indices[i] = i;
        // go to next index
        i = j;
        j = indices[j];
      }
      // put original array[k] back in
      // and update indices
      array[i] = v0;
      indices[i] = i;
    }
  }
  return array;
}
Trabajo
fuente
0

Sé que esto ha recibido muchas respuestas. Solo quería publicar una implementación rápida de TS para cualquiera que haya aterrizado aquí en busca de eso.

export function stableSort<T>( array: T[], compareFn: ( a: T, b: T ) => number ): T[] {
    const indices = array.map( ( x: T, i: number ) => ( { element: x, index: i } ) );

    return indices.sort( ( a, b ) => {
        const order = compareFn( a.element, b.element );
        return order === 0 ? a.index - b.index : order;
    } ).map( x => x.element );
}

El método ya no se ejecuta en el lugar, como lo hace la ordenación nativa. También quiero señalar que no es el más eficiente. Agrega dos bucles del orden O (n). aunque la clasificación en sí misma es probablemente O (n log (n)), por lo que es menor que eso.

Algunas de las soluciones mencionadas son más eficaces, aunque podría ser menos código, también usando internos Array.prototype.sort .

(Para una solución de Javascript, simplemente elimine todos los tipos)

Mathias
fuente
0

De acuerdo con el blog de desarrollo v8 y caniuse.com Array.sort ya es estable como lo requieren las especificaciones en los navegadores modernos, por lo que no necesita desarrollar su propia solución. La única excepción que puedo ver es Edge, que pronto debería pasar al cromo y soportarlo también.

Akaltar
fuente
0

function sort(data){
    var result=[];
    var array = data;
    const array2=data;
    const len=array2.length;
    for(var i=0;i<=len-1;i++){
    var min = Math.min.apply(Math,array)
    result.push(min);
    var index=array.indexOf(min)
    array.splice(index,1);
    }
    return result;
}   
sort([9,8,5,7,9,3,9,243,4,5,6,3,4,2,4,7,4,9,55,66,33,66]);

Appani Kaushik
fuente
-1

La ordenación por conteo es más rápida que la ordenación por fusión (se realiza en tiempo O (n)) y está pensada para su uso en números enteros.

Math.counting_sort = function (m) {
    var i
    var j
    var k
    var step
    var start
    var Output
    var hash
    k = m.length
    Output = new Array ()
    hash = new Array ()
    // start at lowest possible value of m
    start = 0
    step = 1
    // hash all values
    i = 0
    while ( i < k ) {
        var _m = m[i]
        hash [_m] = _m
        i = i + 1
    }
    i = 0
    j = start
    // find all elements within x
    while ( i < k ) {
        while ( j != hash[j] ) {
            j = j + step
        }
        Output [i] = j
        i = i + 1
        j = j + step
    }
    return Output
}

Ejemplo:

var uArray = new Array ()<br/>
var sArray = new Array ()<br/><br/>
uArray = [ 10,1,9,2,8,3,7,4,6,5 ]<br/>
sArray = Math.counting_sort ( uArray ) // returns a sorted array
Jericó Oeste
fuente
12
Algunas cosas que se deben decir: 1. La clasificación por conteo solo funciona bien en un espacio numérico denso. (Intente ordenar la matriz [1, 2e9, 1e9]) 2. No escriba bucles for como bucles while. 3. No agregue cosas al azar al espacio de nombres Math. 4. Es posible que desee considerar la posibilidad de hacerse amigo del punto y coma.
Domi
Además, en caso de que la matriz tenga valores duplicados, se ejecutará para siempre. Por ejemplo, array [3, 1, 3]hashes to [undefined, 1, undefined, 3]. Obtenemos dos valores no indefinidos, mientras que el algoritmo espera que haya tres de ellos.
MKPS