javascript - Classificando números em ordem decrescente, mas com `0`s no início




arrays sorting (7)

Apenas modifique a condição da sua função de comparação assim:

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];
arr.sort((a, b) => {
     if(a && b) return b-a;
     return a-b;
});

console.log(arr);

Tenho um desafio em JavaScript que já estou tentando descobrir há um tempo.

Considere esta matriz:

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

Eu tenho que produzir este resultado:

arr = [0, 0, 0, 0, 0, 5, 4, 3, 2, 1]

Estou seguindo esta linha de lógica para posicionar os zeros na frente, ajustando o valor do índice:

arr.sort((x, y) => {
    if (x !== 0) {
        return 1;
    }

    if (x === 0) {
        return -1;
    }

    return y - x;
});

Mas estou preso neste resultado:

arr = [0, 0, 0, 0, 0, 1, 2, 3, 4, 5]

Alguém tem alguma dica sobre como resolver isso?


Em vez de propor uma nova solução (já existem várias aqui), gostaria apenas de explicar a falha na sua.

Quando você faz:

if (x !== 0) {
    return 1;
}
if (x === 0) {
    return -1;
}

Você está cobrindo todas as situações possíveis! Portanto, o return y - x nunca é alcançado. Podemos provar isso facilmente (a mensagem console.log não será exibida):

arr = [0, 0, 0, 0, 0, 5, 4, 3, 2, 1]

arr.sort((x, y) => {
  if (x !== 0) {
    return 1;
  }
  if (x === 0) {
    return -1;
  }
  console.log("I will never show up")
  return y - x;
});

Portanto, mantendo-o o mais próximo possível do seu código, deve ser:

arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

arr.sort((x, y) => {
  if (x === 0) {
    return -1;
  }
  if (y === 0) {
    return 1;
  }
  return y - x;
});

console.log(arr)

Como você pode ver, a diferença do seu código é de apenas 1 caractere ( ! To = ), então agora estamos testando para qualquer um dos números na comparação ser zero.


Não está jogando código-golfe aqui:

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, -1];
arr.sort(function(a, b) {
  if (a === 0 && b !== 0) {
    // a is zero b is nonzero, a goes first
    return -1;
  } else if (a !== 0 && b === 0) {
    // a is nonzero b is zero, b goes first
    return 1;
  } else {
    // both are zero or both are nonzero, sort descending
    return b - a;
  }
});
console.log(arr.toString());


Se você se preocupa com eficiência, provavelmente é mais rápido filtrar os zeros primeiro . Você não quer perder tempo olhando para eles, muito menos adicionando trabalho extra ao seu retorno de chamada de comparação para lidar com esse caso especial.

Especialmente se você espera um número significativo de zeros, uma passagem sobre os dados para filtrá-los deve ser muito melhor do que fazer uma classificação O (N log N) maior que analisará cada zero várias vezes.

Você pode acrescentar com efficiently o número certo de zeros depois de terminar.

Também é fácil ler o código resultante. Usei o TypedArray porque é eficiente e facilita a classificação numérica . Mas você pode usar essa técnica com Array regular, usando o idioma padrão de (a,b)=>ab para .sort .

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

let nonzero_arr = Int32Array.from(arr.filter(n => n != 0));
let zcount = arr.length - nonzero_arr.length;
nonzero_arr.sort();      // numeric TypedArray sorts numerically, not alphabetically

// Reverse the sorted part before copying into the final array.
nonzero_arr.reverse();

 // efficient-ish TypedArray for main result
let revsorted = new Int32Array(arr.length);   // zero-filled full size
revsorted.set(nonzero_arr, zcount);           // copy after the right number of zeros

console.log(Array.from(revsorted));      // prints strangely for TypedArray, with invented "0", "1" keys

/*
   // regular Array result
let sorted = [...Array(zcount).fill(0), ...nonzero_arr]  // IDK if this is efficient
console.log(sorted);
*/

Não sei se TypedArray .sort() e, em seguida, .reverse é mais rápido do que usar uma função de comparação personalizada para classificar em ordem decrescente. Ou se pudermos copiar e reverter em tempo real com um iterador.

Também vale a pena considerar: use apenas um TypedArray de todo o comprimento .

Em vez de usar .filter , faça um loop sobre ele e troque os zeros para a frente da matriz à medida que avança. Isso leva uma passagem sobre seus dados.

Em seguida, use .subarray() para obter uma nova exibição TypedArray dos elementos diferentes de zero do mesmo ArrayBuffer subjacente. Classificação que deixará a matriz completa com um zero de início e uma cauda classificada, com a classificação apenas olhando para os elementos diferentes de zero.

Não vi uma função de partição nos métodos Array ou TypedArray, mas mal conheço JavaScript. Com um bom JIT, um loop não deve ser muito pior do que um método interno. (Especialmente quando esse método envolve um retorno de chamada como .filter e, a menos que use realloc sob o capô para diminuir, ele precisa descobrir quanta memória alocar antes de realmente filtrar).

Usei regular-Array .filter() antes de converter para um TypedArray. Se sua entrada já é um TypedArray, você não tem esse problema, e essa estratégia se torna ainda mais atraente.


Tente classificar a matriz outra vez:

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

arr.sort((x, y) => {
    return y > x ? 1 : -1;
}).sort((x, y) => {
    if (x === 0) { return -1; } return 0;
})

Você pode classificar pelo delta de b (para classificação decrescente) e pegar Number.MAX_VALUE , para valores falsos como zero.

Este:

Number.MAX_VALUE - Number.MAX_VALUE

é igual a zero.

let array = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

array.sort((a, b) => (b || Number.MAX_VALUE) - (a || Number.MAX_VALUE));

console.log(...array);


Como mdn docs diz:

Se aeb são dois elementos comparados, então:

Se compareFunction(a, b) retornar menor que 0 , classifique a para um índice menor que b (ou seja, a vem primeiro).

Se compareFunction(a, b) retornar 0, deixe aeb inalterados um com o outro, mas classificados com relação a todos os elementos diferentes. Nota: o padrão ECMAscript não garante esse comportamento; portanto, nem todos os navegadores (por exemplo, versões do Mozilla que datam de pelo menos 2003) respeitam isso.

Se compareFunction(a, b) retornar maior que 0, classifique b para um índice menor que a (ou seja, b vem primeiro).

compareFunction(a, b) sempre deve retornar o mesmo valor quando determinado um par específico de elementos b como seus dois argumentos. Se resultados inconsistentes forem retornados, a ordem de classificação será indefinida.

Portanto, a função de comparação tem o seguinte formato:

function compare(a, b) {
  if (a is less than b by some ordering criterion) {
    return -1;
  }
  if (a is greater than b by the ordering criterion) {
    return 1;
  }
  // a must be equal to b
  return 0;
}

let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];

arr.sort((x, y) => {
    if (x > 0 && y > 0) {
        return y - x;
    }
    return x - y;
});

console.log(arr);





sorting