Mam wyzwanie w JavaScript, które próbuję już wymyślić.
Rozważ tę tablicę:
let arr = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5];
Muszę przedstawić ten wynik:
arr = [0, 0, 0, 0, 0, 5, 4, 3, 2, 1]
Podążam tą linią logiczną, aby ustawić zera na wierzchu, dostosowując wartość indeksu:
arr.sort((x, y) => {
if (x !== 0) {
return 1;
}
if (x === 0) {
return -1;
}
return y - x;
});
Ale utknąłem na tym wyniku:
arr = [0, 0, 0, 0, 0, 1, 2, 3, 4, 5]
Czy ktoś ma jakieś wskazówki, jak to rozwiązać?
javascript
arrays
sorting
lianbwl
źródło
źródło
return x - y;
?return y - x;
? Nawet w javascript nie mogę wymyślić niczego, co nie byłoby ani===0
ani!==0
.Odpowiedzi:
Możesz sortować według delty
b
ia
(dla sortowania malejącego) i braćNumber.MAX_VALUE
, dla wartości falsy takich jak zero.To:
jest równy zero.
źródło
NaN
, gdy obaa
ib
są równe zeru. Może to być niepożądane zachowanie.Array.prototype.sort
są zdefiniowane w implementacji, jeśli komparator kiedykolwiek zwróciNaN
, więc ten komparator jest złym pomysłem. Stara się być sprytny i robi to źle.Jak mdn docs mówi:
Jeśli a i b to dwa porównywane elementy, to:
Tak więc funkcja porównania ma następującą postać:
źródło
Jeśli zależy Ci na wydajności, prawdopodobnie najszybciej odfiltruje zera . Nie chcesz
sort
tracić czasu, nawet na nie patrząc, nie mówiąc już o dodaniu dodatkowej pracy do wywołania zwrotnego porównania w celu obsługi tego specjalnego przypadku.Zwłaszcza jeśli spodziewasz się znacznej liczby zer, jedno przejście przez dane w celu ich odfiltrowania powinno być znacznie lepsze niż wykonanie większego sortowania O (N log N), który będzie sprawdzał każde zera wiele razy.
Po zakończeniu możesz skutecznie wstawić poprawną liczbę zer.
Równie łatwo jest odczytać wynikowy kod. Użyłem TypedArray, ponieważ jest wydajny i ułatwia sortowanie numeryczne . Ale możesz użyć tej techniki ze zwykłym Array, używając standardowego idiomu
(a,b)=>a-b
for.sort
.Nie wiem, czy TypedArray,
.sort()
a następnie.reverse
jest szybszy niż używanie niestandardowej funkcji porównywania do sortowania w kolejności malejącej. Lub jeśli możemy kopiować i cofać w locie za pomocą iteratora.Warto również rozważyć: użyj tylko jednego Tablicy Typów o pełnej długości .
Zamiast tego użyj
.filter
pętli i zamień zera na przód tablicy podczas pracy. To zajmuje jedno przejście przez twoje dane.Następnie użyj
.subarray()
aby uzyskać nowy widok TypedArray niezerowych elementów tego samego bazowego ArrayBuffer. Sortowanie, które pozostawia pełną tablicę z zerowym początkiem i posortowanym ogonem, przy czym sortowanie obejmuje tylko niezerowe elementy.Nie widziałem funkcji partycji w metodach Array ani TypedArray, ale ledwo znam JavaScript. Przy dobrym JIT pętla nie powinna być o wiele gorsza niż wbudowana metoda. (Zwłaszcza gdy ta metoda wymaga wywołania zwrotnego
.filter
, i jeśli nie używa sięrealloc
pod maską do zmniejszenia, musi dowiedzieć się, ile pamięci do przydzielenia, zanim faktycznie się odfiltruje).Użyłem tablicy regularnej
.filter()
przed konwersją na tablicę TypedArray. Jeśli Twój wkład jest już TypedArray, nie masz tego problemu, a ta strategia staje się jeszcze bardziej atrakcyjna.źródło
Po prostu zmodyfikuj stan swojej funkcji porównania w ten sposób -
źródło
!a
jest nadal spełniony. Powróci-1
a=b=0
Nie grasz tutaj w golfa kodowego:
źródło
Nie pisz własnego sortowania numerycznego, jeśli już istnieje. To, co chcesz zrobić, to dokładnie to, co mówisz w tytule; sortuj liczby w kolejności malejącej, z wyjątkiem zer na początku.
Nie pisz kodu, którego nie potrzebujesz; możesz to źle pomylić.
Wybierz TypedArray w oparciu o typ liczb, które ma obsługiwać tablica. Float64 jest dobrym domyślnym, ponieważ obsługuje wszystkie normalne liczby JS.
źródło
Array(n).fill(0)
.let f64arr = new Float64Array(arr.filter(n => n != 0))
, to[ ...Array(arr.length - f64arr.length).fill(0),
... tak to dodaje 1 dodatkową linię i upraszcza ostatnią linię.Możesz to zrobić w następujący sposób:
lub możesz to zrobić:
źródło
źródło