๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ‘จ‍๐Ÿ’ป web.dev/frontend

[JavaScript] Array.sort ๋Š” ์•ˆ์ •์„ฑ์„ ๋ณด์žฅํ• ๊นŒ?

by HandHand 2022. 3. 9.

JavaScript ์˜ sort ํ•จ์ˆ˜๋Š” ์•ˆ์ • ์ •๋ ฌ์ผ๊นŒ?

๐Ÿ“Œ ์•ˆ์ • ์ •๋ ฌ์ด๋ž€?

์•ˆ์ • ์ •๋ ฌ(stable sort) ์€ ๋™์ผํ•œ ํ‚ค ๊ฐ’์— ๋Œ€ํ•ด ์ž…๋ ฅ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜๋Š” ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค.

๊ทธ ๋ฐ˜๋Œ€์ธ ๋ถˆ์•ˆ์ • ์ •๋ ฌ(unstable sort) ์€ ์ •๋ ฌ ํ›„ ์ž…๋ ฅ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•ด์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋Œ€ํ‘œ์ ์ธ ์•ˆ์ • ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด 3๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

1๏ธโƒฃ ๋ฒ„๋ธ” ์ •๋ ฌ (Bubble Sort)

1

2๏ธโƒฃ ์‚ฝ์ž… ์ •๋ ฌ (Insertion Sort)

2

3๏ธโƒฃ ๋ณ‘ํ•ฉ ์ •๋ ฌ (Merge Sort)

3

๊ทธ ์™ธ ํ€ต ์ •๋ ฌ ๊ณผ ์„ ํƒ ์ •๋ ฌ ์€ ๋ถˆ์•ˆ์ • ์ •๋ ฌ ์˜ ๋Œ€ํ‘œ์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค.

 

๐Ÿ“Œ JavaScript ์˜ sort ํ•จ์ˆ˜

JavaScript ์˜ sort ํ•จ์ˆ˜๋Š” ์•ˆ์ • ์ •๋ ฌ์ผ๊นŒ?

JavaScript ์˜ sort ํ•จ์ˆ˜๋Š” ๊ทธ๋ ‡๋‹ค๋ฉด ์•ˆ์ • ์ •๋ ฌ ์ผ๊นŒ์š”?

Chrome 70 ๋ฒ„์ „ ์ด์ „์—์„œ๋Š” ์‚ฌ์‹ค JavaScript ์˜ ๊ตฌํ˜„ ์ŠคํŽ™์ƒ ์•ˆ์ • ์ •๋ ฌ์„ ๊ฐ•์š”ํ•˜์ง€ ์•Š์•˜๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๋•Œ๋ฌธ์— ๊ฐ๊ฐ์˜ ์—”์ง„ ๊ตฌํ˜„์ฒด๋งˆ๋‹ค ์ •๋ ฌ ๋ฐฉ์‹์ด ๋‹ค๋ฅด๊ฒŒ ๋˜์–ด์žˆ์œผ๋ฉฐ chrome ์˜ ๊ฒฝ์šฐ์—๋Š” ๋ฐฐ์—ด์˜ ๊ธธ์ด์— ๋”ฐ๋ผ

์•ˆ์ • ์ •๋ ฌ์ผ ์ˆ˜๋„ ์žˆ๊ณ  ๋ถˆ์•ˆ์ • ์ •๋ ฌ์ผ ์ˆ˜๋„ ์žˆ์—ˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ chrome 70 ์ดํ›„ ๋ฒ„์ „๋ถ€ํ„ฐ๋Š” Array.sort ๋ฅผ ์•ˆ์ • ์ •๋ ฌ๋กœ ๊ตฌํ˜„ํ•˜๋„๋ก ์ŠคํŽ™์ด ์ง€์ •๋˜์—ˆ๊ณ 

๊ทธ์— ๋”ฐ๋ผ ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €์—์„œ ์•ˆ์ • ์ •๋ ฌ์ž„์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Chrome 70 ์ด์ „ ์‹œ์ ˆ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜

๋‹ค์Œ์€ chrome 70 ์ด์ „์˜ V8 ์—”์ง„ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์†Œ์Šค์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

// v8/array.js

/**
 * โœ… Array.prototype.sort ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
 */
DEFINE_METHOD(
  GlobalArray.prototype,
  sort(comparefn) {
    if (!IS_UNDEFINED(comparefn) && !IS_CALLABLE(comparefn)) {
      throw %make_type_error(kBadSortComparisonFunction, comparefn);
    }

    var array = TO_OBJECT(this);
    var length = TO_LENGTH(array.length);
    return InnerArraySort(array, length, comparefn);
  }
);

/**
 * โœ… ์‹ค์ œ ์ •๋ ฌ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
 */
function InnerArraySort(array, length, comparefn) {
  // In-place QuickSort algorithm.
  // For short (length <= 10) arrays, insertion sort is used for efficiency.

  if (!IS_CALLABLE(comparefn)) {
    comparefn = /** ๊ธฐ๋ณธ ๋น„๊ต ํ•จ์ˆ˜ ํ• ๋‹น */
  }

  function InsertionSort(a, from, to) {/** */};

  function QuickSort(a, from, to) {
        // ...

        while (true) {
      // Insertion sort is faster for short arrays.
      if (to - from <= 10) {
        InsertionSort(a, from, to);
        return;
      }

        // ...
  };

  if (length < 2) return array;

  QuickSort(array, 0, num_non_undefined);

  return array;
}

์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด Array.prototype.sort ๋Š” ์—”์ง„ ๋‚ด๋ถ€์ ์œผ๋กœ InnerArraySort ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ 

ํ•ด๋‹น ํ•จ์ˆ˜ ๋‚ด์—์„œ ๋ฐฐ์—ด์˜ ๊ธธ์ด๊ฐ€ 10 ๋ณด๋‹ค ์ž‘์„ ๊ฒฝ์šฐ์—๋Š” Insertion Sort ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ 

๊ทธ ์™ธ์—๋Š” Quick Sort ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ดํ›„์˜ v8 ์—”์ง„์€ Timsort ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ด์„œ ์ •๋ ฌํ•ฉ๋‹ˆ๋‹ค

stable ํ•จ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ˜„์žฌ v8 ์—”์ง„์€ TimSort ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ด์šฉํ•ด ์ •๋ ฌ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ํ˜„์žฌ v8 ์—”์ง„ ์†Œ์Šค์˜ ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

ArrayTimSort ๋ฅผ ํ˜ธ์ถœํ•ด์„œ stable ํ•œ ์ •๋ ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// third_party/v8/builtins/array-sort.tq

ArrayPrototypeSort(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  // ...

  if (len < 2) return obj;

  const sortState: SortState = NewSortState(obj, comparefn, len);
  ArrayTimSort(context, sortState);

  return obj;
}

 

๐Ÿ“Œ Chrome 70 ์ด์ „์— ์•ˆ์ •์ •๋ ฌ์„ ๊ตฌํ˜„ํ–ˆ๋˜ ๋ฐฉ๋ฒ•

์•ˆ์ • ์ •๋ ฌ์„ ๊ตฌํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ๊ฐ ๋ฐฐ์—ด ์š”์†Œ์— key ๊ฐ’์„ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‘ ๋ฐฐ์—ด ์š”์†Œ์˜ ๋น„๊ต ํ‚ค๊ฐ’์ด ๋™์ผํ•œ ๊ฒฝ์šฐ์—๋Š” ์ธ๋ฑ์Šค๋กœ ๋น„๊ตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

const stableSort = (arr, compare) =>
  arr
    .map((item, index) => ({ item, index }))
    .sort((a, b) => compare(a.item, b.item) || a.index - b.index)
    .map(({ item }) => item)

 

์œ„ ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ์ •๋ ฌ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

/** โœ… rating ์„ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. */
console.log(
  stableSort(
    [
      { name: "Abby", rating: 12 },
      { name: "Bandit", rating: 13 },
      { name: "Choco", rating: 14 },
      { name: "Daisy", rating: 12 },
      { name: "Elmo", rating: 12 },
      { name: "Falco", rating: 13 },
      { name: "Ghost", rating: 14 },
    ],
    (a, b) => a.rating - b.rating
  )
)[
  /** ๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ •๋ ฌ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. */
  ({ name: "Abby", rating: 12 },
  { name: "Daisy", rating: 12 },
  { name: "Elmo", rating: 12 },
  { name: "Bandit", rating: 13 },
  { name: "Falco", rating: 13 },
  { name: "Choco", rating: 14 },
  { name: "Ghost", rating: 14 })
]

 

๐Ÿ“Œ ์ฐธ๊ณ  ์ž๋ฃŒ

Stable Array.prototype.sort

Fast stable sorting algorithm implementation in javascript

๋ฐ˜์‘ํ˜•

๐Ÿ’ฌ ๋Œ“๊ธ€