Implementação de algoritmo de classificação rápida e estável em javascript

104

Estou procurando classificar uma matriz de cerca de 200-300 objetos, classificando em uma chave específica e uma determinada ordem (asc / desc). A ordem dos resultados deve ser consistente e estável.

Qual seria o melhor algoritmo a ser usado e você poderia fornecer um exemplo de sua implementação em javascript?

Obrigado!

William Casarin
fonte
6
Como pelo menos a classificação de array do Chrome não parece estável, confiar na classificação de array embutida não é uma opção para você.
Nosredna de
Para resumir: optei por uma classificação de mesclagem enrolada manualmente devido às inconsistências de estabilidade de Array.sort entre os navegadores modernos (principalmente o cromo não implementava uma classificação estável no momento deste comentário). Obrigado a todos pela ajuda!
William Casarin,
O que queremos dizer com classificação "estável"?
mowwwalker
2
@mowwwalker A classificação estável é uma classificação em que todos os itens com o mesmo valor de classificação são deixados na mesma ordem que na coleção original. en.wikipedia.org/wiki/Sorting_algorithm#Stability
Kornelije Petak
Para responder "qual é o melhor algoritmo a ser usado", precisamos saber se há alguma estrutura subjacente aos seus dados. Muitas das respostas abaixo falam apenas sobre o uso da classificação por mesclagem ou classificação rápida; na realidade, isso depende dos dados. Não é um problema simples responder, eu não diria. Pesquise no Google alguns algoritmos de classificação e leia sobre eles para ver o que quero dizer. TimSort e Radix Sort são dois bons exemplos sobre os quais eu recomendo ler.
será

Respostas:

114

É possível obter uma classificação estável a partir de uma função de classificação não estável.

Antes de classificar você obtém a posição de todos os elementos. Em sua condição de classificação, se ambos os elementos forem iguais, você classifica pela posição.

Tada! Você tem um tipo estável.

Escrevi um artigo sobre isso no meu blog se você quiser saber mais sobre essa técnica e como implementá-la: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html

Vjeux
fonte
32
Você poderia fornecer a resposta aqui em vez de postar e criar um link para o seu blog! Obrigado
Mohammad Kermani
4
Um link para uma solução é bem-vindo, mas certifique-se de que sua resposta seja útil sem ele: adicione contexto ao link para que seus outros usuários tenham uma ideia do que ele é e por que está lá, depois cite a parte mais relevante da página que você ' novo link para caso a página de destino não esteja disponível. Respostas que são pouco mais do que um link podem ser excluídas.
Samuel Liew
@Vjeux já que isso está se tornando popular, você se importa em colar o código relevante nesta resposta? Isso ajudaria muito! obrigado!
William Casarin
34

Como você está procurando por algo estável, a classificação por mesclagem deve servir.

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

O código pode ser encontrado no site acima:

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:

De acordo com esta postagem , parece que Array.Sort em algumas implementações usa uma classificação por mesclagem.

kemiller2002
fonte
++ O tipo de mesclagem é o meu favorito. É simples e estável, sem piores casos.
Mike Dunlavey,
O link para o site está fora do ar :(
ahitt6345
encontrou um novo site para o exemplo.
kemiller2002
7
Nota: Array#shiftpode funcionar em tempo O (n) e, portanto, você mergeem O (n * n).
4esn0k
22

Versão um pouco mais curta da mesma coisa usando recursos do ES2017, como funções de seta e desestruturação:

Função

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)

Ele aceita matriz de entrada e função de comparação:

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

Ele também retorna uma nova matriz em vez de fazer uma classificação local, como a função Array.sort () embutida.

Teste

Se pegarmos a seguinte inputmatriz, inicialmente classificada 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 }
]

Em seguida, classifique-o heightusando stableSort:

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

Resulta em:

// 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 }
]

No entanto, classificar a mesma inputmatriz usando o integrado Array.sort()(no Chrome / NodeJS):

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

Retorna:

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

Atualizar

Array.prototype.sort agora está estável no V8 v7.0 / Chrome 70!

Anteriormente, o V8 usava um QuickSort instável para arrays com mais de 10 elementos. Agora, usamos o algoritmo TimSort estável.

fonte

simo
fonte
1
A stableSortfunção é realmente uma ótima solução!
lhermann
16

Eu sei que esta pergunta foi respondida há algum tempo, mas acontece que tenho uma boa implementação estável de merge sort para Array e jQuery na minha área de transferência, então vou compartilhá-la na esperança de que alguns pesquisadores futuros possam achar útil.

Ele permite que você especifique sua própria função de comparação, assim como a Array.sortimplementação normal .

Implementação

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

Exemplo 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
fonte
4
Observe que isso está em desacordo com a classificação nativa, que funciona no local, o que significa que não pode ser simplesmente inserido.
Eric
3
Talvez estável, mas este método é cerca de 20 vezes mais lento do que o nativo array.sort, consulte o teste aqui para strings e inteiros -> jsfiddle.net/QC64j
davidkonrad
2
É claro que é mais lento do que o tipo nativo - não é nativo. Isso é impossível. Também é verdade que não faz uma classificação no local. Isso também é impossível (na melhor das hipóteses, você cria uma cópia e sobrescreve o original). Além disso, retornar uma cópia classificada é mais JavaScript, apesar do próprio comportamento de classificação nativo do JavaScript. A função também é chamada mergeSorte não sort, portanto, não tem o objetivo de substituí-la. Às vezes, você só precisa de uma classificação de mesclagem estável, por exemplo, ao classificar tabelas por coluna.
Justin Force
1
Errado, a classificação nativa do Node é escrita em javascript. É inteiramente possível que um algoritmo programado em javascript ultrapasse a classificação nativa. Eu construí um algoritmo de classificação inteiramente em javascript (um tipo de classificação de mesclagem adaptativa) que Kremes / creams / Kreams O quicksort nativo no nó. O objetivo deste comentário é mostrar que nativo não importa no caso de javascript porque o algoritmo de classificação é escrito em javascript e não em alguma linguagem superior como c ++. A prova está aqui: github.com/nodejs/node/blob/master/deps/v8/src/js/array.js
ahitt6345
2
Para quem deseja uma solução imediata e imediata que é muito mais rápida do que essa implementação, verifique minha resposta .
Patrick Roberts,
10

Você pode usar o seguinte polyfill para implementar uma classificação estável, independentemente da implementação nativa, com base na afirmação feita nesta resposta :

// 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) + '%')

Executando os testes de desempenho implementados acima, stableSort()parece funcionar a cerca de 57% da velocidade do sort()Chrome versão 59-61.

O uso .bind(compareFunction)da função anônima encapsulada em stableSort()aumentou o desempenho relativo de cerca de 38%, evitando uma referência de escopo desnecessária a compareFunctionem cada chamada, atribuindo-a ao contexto.

Atualizar

Operador ternário alterado para curto-circuito lógico, que tende a ter um desempenho melhor em média (parece fazer uma diferença de 2-3% na eficiência).

Patrick Roberts
fonte
5

O seguinte classifica a matriz fornecida, aplicando a função de comparação fornecida, retornando a comparação do índice original quando a função de comparação retorna 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;
}

O exemplo abaixo classifica uma matriz de nomes por sobrenome, mantendo a ordem dos sobrenomes iguais:

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
fonte
Não estável para tipos primitivos ou elementos duplicados na matriz. jQuery.expando.split( "" ).sort( ( a, b ) => 0 ).join( "" ) === jQuery.expando
marciano
3

Aqui está uma implementação estável. Ele funciona usando a classificação nativa, mas nos casos em que os elementos são comparados como iguais, você quebra os laços usando a posição do í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;
    });
}

um teste não completo

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

outra resposta aludiu a isso, mas não postou teh codez.

mas não é rápido de acordo com meu benchmark . Modifiquei um impl de classificação de mesclagem para aceitar uma função de comparação personalizada e foi muito mais rápido.

bode
fonte
Seu benchmark está correto? Parece que seu "stableSort" não modifica a matriz de entrada, outras classificações - fazem, e como você não recriou "arr" durante a "configuração", outras classificações classificam as matrizes já classificadas ....
4esn0k
@ 4esn0k você leu errado? Eu disse que minha função stableSort NÃO era rápida.
cabra
@gota, ah, me perdoe
4esn0k
3

Você também pode usar Timsort. Este é um algoritmo muito complicado (mais de 400 linhas, portanto, nenhum código-fonte aqui), portanto, consulte a descrição da Wikipedia ou use uma das implementações JavaScript existentes:

Implementação GPL 3 . Empacotado como Array.prototype.timsort. Parece ser uma reescrita exata do código Java.

Implementação de domínio público Significada como um tutorial, o código de amostra mostra apenas seu uso com inteiros.

Timsort é um híbrido altamente otimizado de mergesort e shuffle sort e é o algoritmo de classificação padrão em Python e em Java (1.7+). É um algoritmo complicado, pois usa algoritmos diferentes para muitos casos especiais. Mas, como resultado, é extremamente rápido em uma ampla variedade de circunstâncias.

David Leppik
fonte
1

Um mergeSort simples 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));
demosthenes
fonte
0

Tenho que classificar as matrizes multidimensionais por uma coluna arbitrária e depois por outra. Eu uso esta função para classificar:

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 retorna zero, que é onde algumas implementações da função "sort" tomam decisões que podem não estar certas.

Isso é muito rápido também.

alfadog67
fonte
0

Veja como você pode estender o objeto Array padrão JS com um método de protótipo utilizando MERGE SORT . Este método permite a classificação em uma chave específica (primeiro parâmetro) e uma determinada ordem ('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)
  }
}

Você pode testar isso colocando o snippet acima no console do seu navegador e, em seguida, tentando:

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]

Ou faça o pedido com base em um campo específico em uma 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
fonte
0

Então eu precisava de uma classificação estável para meu aplicativo React + Redux, e a resposta de Vjeux aqui me ajudou. No entanto, minha solução (genérica) parece diferente das outras que vi aqui até agora, então estou compartilhando-a caso alguém tenha um caso de uso correspondente:

  • Eu realmente só quero ter algo semelhante à sort()API, onde eu possa passar uma função de comparador.
  • Às vezes eu posso classificar no local e às vezes meus dados são imutáveis ​​(porque Redux) e, em vez disso, preciso de uma cópia classificada. Portanto, preciso de uma função de classificação estável para cada caso de uso.
  • ES2015.

Minha solução é criar uma matriz digitada de e indices, em seguida, usar uma função de comparação para classificar esses índices com base na matriz a ser classificada. Em seguida, podemos usar o classificado indicespara classificar o array original ou criar uma cópia classificada em uma única passagem. Se isso for confuso, pense desta forma: onde você normalmente passaria uma função de comparação como:

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

Agora você usa:

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

A velocidade é boa; é basicamente o algoritmo de classificação embutido, mais duas passagens lineares no final e uma camada extra de sobrecarga de indireção do ponteiro.

Fico feliz em receber feedback sobre esta abordagem. Aqui está minha implementação completa disso:

/**
 * - `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;
}
Trabalho
fonte
0

Eu sei que isso foi bastante respondido. Eu só queria postar uma implementação rápida do TS para qualquer um que veio aqui procurando por isso.

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

O método não é mais executado no local, como a classificação nativa. Também quero ressaltar que não é o mais eficiente. Ele adiciona dois loops da ordem O (n). embora a própria classificação seja provavelmente O (n log (n)), portanto, é menor que isso.

Algumas das soluções mencionadas são mais eficientes, embora isso possa ser menos código, também usando interno Array.prototype.sort.

(Para uma solução Javascript, basta remover todos os tipos)

Mathias
fonte
0

De acordo com o v8 dev blog e caniuse.com Array.sort já é estável conforme exigido pelas especificações em navegadores modernos, então você não precisa lançar sua própria solução. A única exceção que vejo é o Edge, que em breve passará para o cromo e também o suportará.

Akaltar
fonte
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
fonte
-1

A classificação por contagem é mais rápida do que a classificação por mesclagem (ela é executada no tempo O (n)) e deve ser usada em inteiros.

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
}

Exemplo:

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
Jericho West
fonte
12
Algumas coisas a serem ditas: 1. A classificação por contagem só funciona bem em um espaço numérico denso. (Tente classificar o array [1, 2e9, 1e9]) 2. Não escreva para loops como loops while. 3. Não adicione coisas aleatoriamente ao namespace do Math. 4. Você pode querer fazer amizade com ponto-e-vírgula.
Domi
Além disso, no caso de array ter valores duplicados, ele será executado para sempre. Por exemplo, array [3, 1, 3]hashes para [undefined, 1, undefined, 3]. Obtemos dois valores não indefinidos, enquanto o algoritmo espera que haja três deles.
MKPS de