Skip to content

Commit

Permalink
update(JS): web/javascript/reference/global_objects/array/sort (#595)
Browse files Browse the repository at this point in the history
* update(JS): web/javascript/reference/global_objects/array/sort

* update(JS): web/javascript/reference/global_objects/array/sort
  • Loading branch information
undead404 authored Sep 7, 2022
1 parent fe72334 commit 0abd4c5
Showing 1 changed file with 20 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,22 @@ sort(function compareFn(a, b) {

## Опис

Якщо `compareFunction` передано не було, всі елементи масиву, котрі не є `undefined`, сортуються шляхом перетворення їх на рядки та порівняння цих рядків в порядку кодів UTF-16. Наприклад, "банан" йде перед "черешнею". У разі сортування чисел – 9 йде перед 80, але через те, що числа перетворюються на рядки перед сортуванням, "80" йде перед "9" згідно з послідовністю кодів Unicode. Всі елементи, які є `undefined`, складаються в кінець масиву.
Якщо `compareFn` передано не було, всі елементи масиву, котрі не є `undefined`, сортуються шляхом перетворення їх на рядки та порівняння цих рядків в порядку кодів UTF-16. Наприклад, "банан" йде перед "черешнею". У разі сортування чисел – 9 йде перед 80, але через те, що числа перетворюються на рядки перед сортуванням, "80" йде перед "9" згідно з послідовністю кодів Unicode. Всі елементи, які є `undefined`, складаються в кінець масиву.

> **Примітка:** В UTF-16 символи юнікоду вище `\uFFFF` кодуються як сурогатні пари кодів з проміжку `\uD800`-`\uDFFF`. Під час порівняння значення кожного коду такої пари враховується окремо. Таким чином, символ, сформований сурогатною парою `\uD855\uDE51`, під час сортування опиниться перед символом `\uFF3A`.
Якщо було передано функцію порівняння `compareFunction`, всі елементи масиву, котрі не є `undefined`, сортуються відповідно поверненого значення функції порівняння (всі елементи, які містять `undefined`, складаються в кінець масиву без викликання `compareFunction`).
Якщо було передано функцію порівняння `compareFn`, всі елементи масиву, котрі не є `undefined`, сортуються відповідно поверненого значення функції порівняння (всі елементи, які містять `undefined`, складаються в кінець масиву без викликання `compareFn`).

| Повернене значення `compareFunction(a, b)` | Порядок сортування |
| ------------------------------------------ | ------------------------------------- |
| > 0 | сортує `a` після `b` |
| < 0 | сортує `a` перед `b` |
| === 0 | зберігає початковий порядок `a` і `b` |
| Повернене значення `compareFn(a, b)` | Порядок сортування |
| ------------------------------------ | ------------------------------------- |
| > 0 | сортує `a` після `b` |
| < 0 | сортує `a` перед `b` |
| === 0 | зберігає початковий порядок `a` і `b` |

Отже, функція порівняння має наступну форму:

```js
function compare(a, b) {
function compareFn(a, b) {
if (a менше за b за якимось критерієм впорядкуваня) {
return -1;
}
Expand All @@ -88,9 +88,9 @@ function compare(a, b) {

- _Чистоту_: Компаратор не змінює об'єкти, що порівнюються, чи будь-який зовнішній стан. (Це важливо, адже немає гарантій того, _коли_ і _як_ компаратор буде викликаний, тож будь-який конкретний виклик не повинен породжувати видимих зовні ефектів.)
- _Стабільність_: Компаратор повертає однаковий результат для кожної пари елементів.
- _Рефлексивність_: `compare(a, a) === 0`.
- _Симетричність_: `compare(a, b) === 0` і `compare(b, a) === 0` повинні або обидвоє бути нулями, або мати протилежні знаки.
- _Транзитивність_: Якщо і `compare(a, b)`, і `compare(b, c)` водночас є або додатними значеннями, або нулями, або від'ємними, то `compare(a, c)` матиме такий самий знак, як і попередні двоє.
- _Рефлексивність_: `compareFn(a, a) === 0`.
- _Симетричність_: `compareFn(a, b) === 0` і `compareFn(b, a) === 0` повинні або обидвоє бути нулями, або мати протилежні знаки.
- _Транзитивність_: Якщо і `compareFn(a, b)`, і `compareFn(b, c)` водночас є або додатними значеннями, або нулями, або від'ємними, то `compareFn(a, c)` матиме такий самий знак, як і попередні двоє.

Компаратор, що відповідає обмеженням вище, обов'язково в деяких випадках повертатиме `1`, `0` і `-1`, або ж він постійно повертатиме `0`. Наприклад, якщо компаратор повертає лише `1` і `0`, або ж якщо повертає лише `0` і `-1`, він не зможе надійно сортувати, бо _симетричність_ буде порушена. Компаратор, що завжди повертає `0`, призведе до того, що масив узагалі не зміниться, але буде в цьому надійним.

Expand Down Expand Up @@ -187,7 +187,7 @@ mixedNumericArray.sort(compareNumbers); // [1, 5, '9', 40, '80', 200, '700']

### Сортування не-ASCII символів

Для сортування рядків з не-ASCII символами, наприклад, рядків з діакритичними знаками (e, é, è, a, ä, та ін.) чи рядків з інших мов, не англійської, використовуйте {{jsxref("String.localeCompare")}}. Ця функція дає змогу порівнювати саме такі символи, так що вони опиняться у вірній послідовності.
Для сортування рядків з не-ASCII символами, наприклад, рядків з діакритичними знаками (e, é, è, a, ä, та ін.) чи рядків з інших мов, не англійської, використовуйте {{jsxref("String.prototype.localeCompare()")}}. Ця функція дає змогу порівнювати саме такі символи, так що вони опиняться у вірній послідовності.

```js
const items = ['réservé', 'premier', 'communiqué', 'café', 'adieu', 'éclair'];
Expand All @@ -198,7 +198,7 @@ items.sort((a, b) => a.localeCompare(b));

### Сортування з `map`

Функція порівняння `compareFunction` може закликатися декілька разів на елемент масиву. Залежно від природи `compareFunction`, це може призвести до високих накладних витрат. Чим більше роботи виконує функція `compareFunction`, і чим більше елементів треба відсортувати, тим ефективніше буде застосувати [`map()`](/uk/docs/Web/JavaScript/Reference/Global_Objects/Array/map) для сортування. Ідея полягає в тому, щоб перебрати масив один раз, щоб витягнути значення для сортування у тимчасовий масив, потім відсортувати тимчасовий масив, і далі перебрати тимчасовий масив іще раз, для отримання правильної послідовності.
Функція порівняння `compareFn` може закликатися декілька разів на елемент масиву. Залежно від природи `compareFn`, це може призвести до високих накладних витрат. Чим більше роботи виконує функція `compareFn`, і чим більше елементів треба відсортувати, тим ефективніше буде застосувати [`map()`](/uk/docs/Web/JavaScript/Reference/Global_Objects/Array/map) для сортування. Ідея полягає в тому, щоб перебрати масив один раз, щоб витягнути значення для сортування у тимчасовий масив, потім відсортувати тимчасовий масив, і далі перебрати тимчасовий масив іще раз, для отримання правильної послідовності.

```js
// масив, який треба відсортувати
Expand Down Expand Up @@ -249,7 +249,7 @@ console.log(numbers[0]); // 3

### Стабільність сортування

Починаючи з версії 10 (або EcmaScript 2019), [специфікація (англ.)](https://tc39.es/ecma262/#sec-array.prototype.sort) постулює, що `Array.prototype.sort` дає стабільне сортування.
Починаючи з версії 10 (або ECMAScript 2019), [специфікація (англ.)](https://tc39.es/ecma262/#sec-array.prototype.sort) постулює, що `Array.prototype.sort` дає стабільне сортування.

Для прикладу, скажімо, є список студентів з їхніми оцінками. Зауважте, що цей список вже відсортований за іменами в алфавітному порядку:

Expand Down Expand Up @@ -281,7 +281,7 @@ students.sort((firstItem, secondItem) => firstItem.grade - secondItem.grade);

Важливо зауважити, що студенти, які мають однакові оцінки (для прикладу, Олексій та Рустем), залишаться в тому ж порядку, що й перед сортуванням. Це те, що гарантує алгоритм стабільного сортування.

До версії 10 (чи EcmaScript 2019) стабільність сортування не гарантувалась, тобто могла трапитись наступна ситуація:
До версії 10 (чи ECMAScript 2019) стабільність сортування не гарантувалась, тобто могла трапитись наступна ситуація:

```js
[
Expand All @@ -300,18 +300,18 @@ students.sort((firstItem, secondItem) => firstItem.grade - secondItem.grade);

```js
const arr = [3, 1, 4, 1, 5, 9];
const compare = (a, b) => (a > b ? 1 : 0);
arr.sort(compare);
const compareFn = (a, b) => (a > b ? 1 : 0);
arr.sort(compareFn);
```

Тут функція `compare` є погано сформованою, тому що не задовольняє правилу симетричності: якщо `a > b`, вона повертає `1`; однак якщо поміняти `a` й `b` місцями, вона замість від'ємного значення повертає `0`. Таким чином, результівний масив буде різним на різних рушіях. Наприклад, V8 (що використовується Chrome, Node.js тощо) та JavaScriptCore (використовується Safari) не сортуватимуть масив узагалі й повернуть `[3, 1, 4, 1, 5, 9]`, натомість SpiderMonkey (використовується Firefox) поверне масив, відсортований в порядку зростання: `[1, 1, 3, 4, 5, 9]`.

А проте, якщо трохи змінити функцію `compare`, щоб вона повертала або `-1`, або `0`:
А проте, якщо трохи змінити функцію `compareFn`, щоб вона повертала або `-1`, або `0`:

```js
const arr = [3, 1, 4, 1, 5, 9];
const compare = (a, b) => (a > b ? -1 : 0);
arr.sort(compare);
const compareFn = (a, b) => (a > b ? -1 : 0);
arr.sort(compareFn);
```

Тоді V8 і JavaScriptCore відсортують масив у порядку спадання: `[9, 5, 4, 3, 1, 1]`, натомість SpiderMonkey поверне його як є: `[3, 1, 4, 1, 5, 9]`.
Expand Down

0 comments on commit 0abd4c5

Please sign in to comment.