내림차순으로 시작하지만 '0'은 시작에 정렬


36

JavaScript에서 이미 한동안 알아 내려고 노력하고 있습니다.

이 배열을 고려하십시오.

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

이 결과를 출력해야합니다.

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

이 논리 라인을 따라 0을 앞에 놓고 인덱스 값을 조정합니다.

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

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

    return y - x;
});

그러나 나는이 결과에 갇혀있다.

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

누구든지 이것을 해결하는 방법에 대한 팁이 있습니까?


6
음수가 없다는 것이 보장됩니까?
마이클 - 클레이 셔키는 어디

2
마지막 줄이로 바뀌면 해결되지 return x - y;않습니까?
Mooing Duck

2
JavaScript에서 0을 세고 제거하고 나머지 요소를 정상적으로 정렬하는 것이 효율적입니까? (커스텀 비교기가 없으면 JS 엔진이 내장 번호 정렬 기능을 사용할 수 있기를 바랍니다). 그런 다음 결과에 올바른 수의 0을 추가하십시오. 0이 많은 경우 정렬하기 전에 제거하면 문제가 줄어 듭니다. 또는 하나의 패스로 0을 배열의 시작으로 바꾸고 배열의 꼬리를 정렬 하시겠습니까?
Peter Cordes

2
이것조차도 도착 return y - x;합니까? 심지어 자바 스크립트, 나는 어느 쪽도 없을 것 아무것도 생각할 수 없다 ===0아니다 !==0.
George T

답변:


35

ba(내림차순 정렬) 의 델타를 기준으로 정렬하고 Number.MAX_VALUE0과 같은 잘못된 값을 취할 수 있습니다 .

이:

Number.MAX_VALUE - Number.MAX_VALUE

0과 같습니다.

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


13
귀하의 비교 함수는 반환 NaN둘 경우 ab제로입니다. 이것은 바람직하지 않은 동작 일 수 있습니다.
dan04

20
Array.prototype.sort비교기가을 반환하면 결과 는 구현에 따라 정의 NaN되므로이 비교기는 나쁜 생각입니다. 그것은 영리하려고 노력하고 틀리게합니다.
user2357112는 Monica

3
배열 내의 요소가 무한대이면 어떻게 되나요? Infinity 숫자를 0과 비교할 때 비교 함수는 0을 반환합니다.
Marco

4
모두 감사합니다. 나는 스스로 뺄 수있는 가장 큰 수를 취하고이 숫자가 op의 배열에 사용되지 않기를 바랍니다.
Nina Scholz

4
절대 읽을 수 없습니다. 시원하지만 읽을 수 없습니다.
살만 A

24

MDN 문서는 다음과 같이 말합니다.

a와 b가 비교되는 두 요소 인 경우 :

경우 compareFunction(a, b)반환 미만 0, 일종의 a인덱스보다 낮은 b(즉, 먼저 온다).

compareFunction(a, b)0을 반환 하면 a와 b는 서로 변경되지 않고 모든 다른 요소에 대해 정렬됩니다. 참고 : ECMAscript 표준은이 동작을 보증하지 않으므로 모든 브라우저 (예 : 2003 년 이전의 Mozilla 버전)가이를 존중하지는 않습니다.

compareFunction(a, b) 0보다 큰 값을 반환 하면 ba보다 낮은 인덱스를 기준으로 정렬 하십시오 (즉, b우선).

compareFunction(a, b)요소의 특정 쌍 주어 졌을 때 항상 같은 값을 반환해야 a하고 b두 개의 인수로합니다. 일치하지 않는 결과가 반환되면 정렬 순서가 정의되지 않은 것입니다.

따라서 비교 함수의 형식은 다음과 같습니다.

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


3
음수를 포함하여 모든 입력에 대해 실제로 일관성있는 ( 표준에 정의 된) 비교 함수를 사용하여 지금까지 유일한 솔루션으로 보이는 것을 +1 .
일 마리 카로 넨

3
@IlmariKaronen : 불행하게도, 비교는 음수에 대해 일관 되지만, 음수에 대해서는 잘못 비교 됩니다. 이 비교기를 사용하여 음수를 0보다 먼저 정렬하고 오름차순으로 정렬합니다.
user2357112는

2
그럼에도 불구하고 OP는 음수에 대해 원하는 동작을 지정하지 않았으므로이 프로토 타입을 사용하면 OP의 요구 사항에 맞게 음수 동작을 조정하는 것이 쉽지 않습니다. 이 답변의 장점은 관용적이고 표준 비교 기능을 사용하여 작업을 수행한다는 것입니다.
J ...

13

효율성에 관심이 있다면 0을 먼저 필터링하는 것이 가장 빠를 것입니다 . sort특별한 경우를 처리하기 위해 비교 콜백에 추가 작업을 추가하는 것 외에도 시간을 낭비 하고 싶지는 않습니다 .

특히 많은 수의 0을 예상하는 경우 각 0을 여러 번 볼 수있는 더 큰 O (N log N) 정렬을 수행하는 것보다 필터링하여 데이터를 한 번 통과하는 것이 훨씬 좋습니다.

완료 후 올바른 수의 0을 효율적으로 추가 할 수 있습니다 .

결과 코드를 읽는 것만 큼 쉽습니다. 효율적이고 숫자 정렬을 쉽게하기 때문에 TypedArray를 사용했습니다 . 그러나 (a,b)=>a-bfor 의 표준 관용구를 사용하여이 기술을 일반 Array와 함께 사용할 수 있습니다 .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);
*/

하는 TypedArray는 나도 몰라 .sort()하고 .reverse빠른 내림차순으로 정렬하는 사용자 정의 비교 함수를 사용하는 것보다. 또는 반복자를 사용하여 즉석에서 복사 및 반전 할 수 있다면.


또한 고려할 가치가 있습니다. 전체 길이 중 하나의 TypedArray 만 사용하십시오 .

사용하는 대신 .filter, 그 위에 루프 및 교환 이가는대로 배열의 전면에 0을. 이것은 데이터를 한 번 넘깁니다.

그런 다음을 사용 .subarray()하여 동일한 기본 ArrayBuffer의 0이 아닌 요소에 대한 새로운 TypedArray 뷰를 가져옵니다. 정렬하면 0이 아닌 요소 만 볼 수 있으며 시작이 0이고 꼬리가 정렬 된 전체 배열이됩니다.

Array 또는 TypedArray 메서드에서 파티션 함수를 보지 못했지만 JavaScript는 거의 알지 못합니다. 좋은 JIT를 사용하면 루프가 내장 메소드보다 훨씬 나빠서는 안됩니다. (특히 해당 메소드에 콜백이 포함 되어 있고 후드에서 축소하기 위해 .filter사용하지 않는 한 realloc실제로 필터링하기 전에 할당 할 메모리 양을 파악해야합니다).

TypedArray로 변환하기 .filter() 전에 regular-Array 사용했습니다 . 입력이 이미 TypedArray 인 경우이 문제가 없으며이 전략이 더욱 매력적입니다.


이것은 아마도 더 단순하거나 관용적이거나 최소한 더 작을 수 있습니다. JS 엔진이 많은 복사 또는 제로 초기화를 제거 / 최적화하지 못하거나 TypedArray 메소드 대신 루프를 사용하는 데 도움이되는 경우 IDK (예 : reverse + .set 대신 역방향 복사). 나는 속도 테스트를하지 않았다. 누군가 그렇게하고 싶다면 관심이 있습니다.
Peter Cordes

@Salman의 테스트 에 따르면 이 답변은 작은 배열 (~ 1000 요소 중 10 %는 0)에 대한 쓰레기처럼 수행됩니다. 아마도 새로운 배열의 모든 생성은 아프다. 아니면 TypedArray와 regular를 부주의하게 혼합합니까? TypedArray 내부의 제로 및 0이 아닌 + 하위 배열 정렬로의 내부 파티션은 내 대답의 두 번째 섹션에서 제안한 것처럼 훨씬 좋습니다.
Peter Cordes

8

다음과 같이 비교 함수의 조건을 수정하십시오.

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

console.log(arr);


6
입력이 음수 일 수있는 경우 일관된 비교기가 아닙니다. 하여 비교기에있어서, 정렬 정렬 0 1 -1 전에 전에 어떤 종류 0 전
Ilmari 카로 넨

음성 입력과 업데이트
하룬 어 라시드

@SalmanA, 둘 다 0이면 조건 !a이 여전히 참입니다. 그것은 돌아올 것이다-1
Harunur Rashid

나는 큰 문제는이 먹으 렴 생각하고 모두 b를 제로 @SalmanA 있습니다 해달라고
하룬 어 라시드

1
정확하게 대답을 편집했습니다. 단지에 대한 조건을 추가a=b=0
하룬 어 라시드

6

코드 골프를하지 않는 경우 :

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


5

이미 존재하는 숫자 정렬을 쓰지 마십시오. 당신이하고 싶은 것은 제목에서 정확히 말한 것입니다. 시작시 0을 제외하고 내림차순으로 숫자를 정렬하십시오.

const zeroSort = arr => [...arr.filter(n => n == 0),
                         ...new Float64Array(arr.filter(n => n != 0)).sort().reverse()];

console.log(zeroSort([0, 1, 0, 2, 0, 3, 0, 4, 0, 500]));

필요하지 않은 코드는 작성하지 마십시오. 잘못 될 수도 있습니다.

배열이 처리 할 숫자 유형에 따라 TypedArray 를 선택하십시오 . Float64는 모든 일반 JS 번호를 처리하므로 좋은 기본값입니다.


@IlmariKaronen 더 나은가?
JollyJoker

두 번 필터링 할 필요가 없습니다. 내 대답에 따르면 한 번 필터링하고 길이를 빼서 숫자를 얻을 수 있습니다 Array(n).fill(0).
Peter Cordes

@PeterCordes 더 간단하다고 할 수 있지만 코드가 읽기 쉽지 않을만큼
길어질 것 같습니다

예, 효율성은 tmp var에 대해 1 개의 추가 라인이 필요합니다. 내 대답은 C 및 C ++ 및 어셈블리 언어에 익숙하기 때문에 여러 개의 추가 행을 사용하며 더 많은 별도의 행을 사용하면 컴파일러 생성 asm을 추적 / 최적화 할 때 책임이있는 명령문으로 쉽게 추적 할 수 있습니다. 또한 나는 일반적으로 JS를 사용하지 않으므로 두 줄에 모두 넣을 필요가 없습니다. 하지만 당신은 할 수있는 let f64arr = new Float64Array(arr.filter(n => n != 0))다음 [ ...Array(arr.length - f64arr.length).fill(0),은 1 개 여분의 라인을 추가하고 마지막 줄을 단순화 있도록 ....
Peter Cordes

4

다음과 같이 할 수 있습니다 :

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

let result = arr.sort((a,b) => {
  if(a == 0 || b == 0)
    return a-b;
  return b-a;
})
console.log(result)

또는 당신은 이것을 할 수 있습니다 :

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

let result = arr.sort().sort((a,b) => {
  if(a > 0 && b > 0)
    return b-a
  return 0
})

console.log(result)


4
첫 번째 솔루션은 Harunur Rashid의 답변과 음수 입력의 일관성 문제가 있습니다. 두 번째 일관성이 있지만 모든 음수를 0으로 처리하여 상호 정렬 순서를 정의하지 않은 채로 둡니다.
Ilmari Karonen

-2

const myArray = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];
const splitArray = myArray.reduce((output, item) => {
     if(!item) output[0].push(item);
    else output[1].push(item);
    return output;
}, [[], []]);
const result = [...splitArray[0], ...splitArray[1].reverse()]
console.log(result);

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.