1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ソート拡張方法【TypeScript】

Last updated at Posted at 2024-03-20

ソート関数を書くときにいろいろと機能を追加したくなるが
ベースの関数を再利用する形で拡張する方法を調べてみた

試したことは以下

  • 引数を増やしていくパターン(失敗)
  • 引数を分割していくパターン(成功?)

前提条件

以下の関数への適用を考える

const array = make_data(100)
array.sort(sort_function)

引数を増やしていくパターン(失敗)

ソート関数1

基本のソート関数

import { describe, it } from 'vitest'

function make_data(N: number) {
  const array = new Array(N)
  for (let i = 0; i < N; ++i) array[i] = Math.floor(Math.random() * N)
  return array
}

function test_sort_basic(a: number, b: number) {
  return a - b
}

describe('引数を増やしていくパターン(失敗)', () => {
  it('1-基本のソート関数', () => {
    const array = make_data(100)
    console.log(array)
    const sort_function = test_sort_basic
    array.sort(sort_function)
    console.log(array)
  })
})

実行結果

[
   4, 51, 28, 62, 10, 61, 79, 53, 24, 27, 86,  6,
  39,  0, 93, 48, 71,  1, 58, 24, 63,  3, 24,  6,
  52, 80, 34, 86,  9, 42, 14, 13, 61, 96,  0, 53,
  92, 33, 57, 35, 90, 13, 73, 20, 96,  2, 18, 92,
  20, 29, 43, 91, 51, 38, 41, 96, 45, 91, 26, 60,
  90, 16, 78, 68, 34,  2, 26, 12, 53, 13, 83, 19,
  45, 15, 89, 64, 18, 78, 63, 96, 92, 13, 98, 98,
  34, 69, 27, 60, 26, 21, 95, 19, 73,  9,  3,  7,
  46, 67, 93,  5
]
[
   0,  0,  1,  2,  2,  3,  3,  4,  5,  6,  6,  7,
   9,  9, 10, 12, 13, 13, 13, 13, 14, 15, 16, 18,
  18, 19, 19, 20, 20, 21, 24, 24, 24, 26, 26, 26,
  27, 27, 28, 29, 33, 34, 34, 34, 35, 38, 39, 41,
  42, 43, 45, 45, 46, 48, 51, 51, 52, 53, 53, 53,
  57, 58, 60, 60, 61, 61, 62, 63, 63, 64, 67, 68,
  69, 71, 73, 73, 78, 78, 79, 80, 83, 86, 86, 89,
  90, 90, 91, 91, 92, 92, 92, 93, 93, 95, 96, 96,
  96, 96, 98, 98
]

ソート関数2

uncurry_sort_basic_2のような変換を毎回作成する必要があり、失敗したような気がする
昇順、降順を追加したソート関数

import { describe, it } from 'vitest'

function make_data(N: number) {
  const array = new Array(N)
  for (let i = 0; i < N; ++i) array[i] = Math.floor(Math.random() * N)
  return array
}

type type_sort_basic_2 = (asc: boolean, a: number, b: number) => number
const uncurry_sort_basic_2 = (f: type_sort_basic_2) => (asc: boolean) => (a: number, b: number) =>
  f(asc, a, b)
  
function test_sort_basic(a: number, b: number) {
  return a - b
}

function test_sort_basic_2(asc: boolean, a: number, b: number) {
  if (asc) return test_sort_basic(a, b)
  else return test_sort_basic(b, a)
}

describe('引数を増やしていくパターン(失敗)', () => {
  it('2-昇順、降順つきのソート関数', () => {
    const array = make_data(100)
    console.log(array)
    const sort_function = uncurry_sort_basic_2(test_sort_basic_2)(false)
    array.sort(sort_function)
    console.log(array)
  })
})

実行結果

[
  37,  3, 63, 50, 75, 24, 43,  4, 38, 88, 57, 56,
  20, 72, 38, 22, 15, 12, 39, 14, 64, 97, 73,  3,
  54, 39, 78, 74, 65, 28, 74, 51, 12, 60, 26, 80,
  89, 99, 56, 56, 66, 89, 74, 92, 45, 20, 62, 94,
  98, 52, 56,  0, 12,  6, 65,  5, 23, 45, 99, 93,
  22, 19, 80, 31, 28, 84, 58, 92, 54, 53, 64, 28,
  25, 77, 14, 53, 37, 63, 41, 22, 86, 84, 12, 25,
  60,  0, 12, 60,  7, 84, 43, 50,  4,  7, 10, 77,
  35, 68, 88, 44
]
[
  99, 99, 98, 97, 94, 93, 92, 92, 89, 89, 88, 88,
  86, 84, 84, 84, 80, 80, 78, 77, 77, 75, 74, 74,
  74, 73, 72, 68, 66, 65, 65, 64, 64, 63, 63, 62,
  60, 60, 60, 58, 57, 56, 56, 56, 56, 54, 54, 53,
  53, 52, 51, 50, 50, 45, 45, 44, 43, 43, 41, 39,
  39, 38, 38, 37, 37, 35, 31, 28, 28, 28, 26, 25,
  25, 24, 23, 22, 22, 22, 20, 20, 19, 15, 14, 14,
  12, 12, 12, 12, 12, 10,  7,  7,  6,  5,  4,  4,
   3,  3,  0,  0
]

引数を分割していくパターン(成功?)

ソート関数2

昇順、降順を追加したソート関数

import { describe, it } from 'vitest'

function make_data(N: number) {
  const array = new Array(N)
  for (let i = 0; i < N; ++i) array[i] = Math.floor(Math.random() * N)
  return array
}

function test_sort_basic(a: number, b: number) {
  return a - b
}

const test_sort_hof_2 = (asc: boolean) => (a: number, b: number) => {
  if (asc) return test_sort_basic(a, b)
  else return test_sort_basic(b, a)
}

describe('引数を分割していくパターン(成功?)', () => {
  it('2-昇順、降順つきのソート関数', () => {
    const array = make_data(100)
    console.log(array)
    const sort_function = test_sort_hof_2(false)
    array.sort(sort_function)
    console.log(array)
  })
})

実行結果

[
  88, 53, 15, 61, 75, 43,  0, 34, 63, 42, 60, 58,
  31, 89, 84, 76,  8, 60, 96, 33, 46, 47, 88, 71,
  40, 88, 14, 91, 88, 42, 85, 66, 70, 15, 35, 73,
   0, 59, 55, 13, 70,  9, 69, 19,  7, 93, 96, 73,
  27, 55, 36, 14, 77,  8, 20,  9, 54, 97, 74, 71,
  79, 17, 69, 49, 72, 88, 94, 72, 71, 41, 27, 23,
  35, 75, 76, 60, 90, 37, 19, 54, 12, 25, 30, 41,
  55, 74, 68,  9, 36, 25, 75, 57, 93, 39, 14, 14,
  73, 42, 49, 59
]
[
  97, 96, 96, 94, 93, 93, 91, 90, 89, 88, 88, 88,
  88, 88, 85, 84, 79, 77, 76, 76, 75, 75, 75, 74,
  74, 73, 73, 73, 72, 72, 71, 71, 71, 70, 70, 69,
  69, 68, 66, 63, 61, 60, 60, 60, 59, 59, 58, 57,
  55, 55, 55, 54, 54, 53, 49, 49, 47, 46, 43, 42,
  42, 42, 41, 41, 40, 39, 37, 36, 36, 35, 35, 34,
  33, 31, 30, 27, 27, 25, 25, 23, 20, 19, 19, 17,
  15, 15, 14, 14, 14, 14, 13, 12,  9,  9,  9,  8,
   8,  7,  0,  0
]

ソート関数3

昇順、降順と特定の値を先頭か末尾に持っていくを追加したソート関数

import { describe, it } from 'vitest'

function make_data(N: number) {
  const array = new Array(N)
  for (let i = 0; i < N; ++i) array[i] = Math.floor(Math.random() * N)
  return array
}

function test_sort_basic(a: number, b: number) {
  return a - b
}

const test_sort_hof_2 = (asc: boolean) => (a: number, b: number) => {
  if (asc) return test_sort_basic(a, b)
  else return test_sort_basic(b, a)
}

const test_sort_hof_3 = (pick: number[] | number) => (asc: boolean) => (a: number, b: number) => {
  if (Array.isArray(pick)) {
    const is_pick_a = pick.includes(a)
    const is_pick_b = pick.includes(b)
    if (is_pick_a && is_pick_b && a !== b) return test_sort_hof_2(asc)(a, b)
    else if (is_pick_a) return asc ? -1 : 1
    else if (is_pick_b) return asc ? 1 : -1
    else return test_sort_hof_2(asc)(a, b)
  } else {
    if (a == pick) return asc ? -1 : 1
    else if (b == pick) return asc ? 1 : -1
    else return test_sort_hof_2(asc)(a, b)
  }
}

describe('引数を分割していくパターン(成功?)', () => {
  it('昇順、降順と特定の値1つを先頭か末尾に持っていくソート関数', () => {
    const array = make_data(100)
    console.log(array)
    const sort_function = test_sort_hof_3(7)(true)
    array.sort(sort_function)
    console.log(array)
  })
  it('昇順、降順と特定の値複数を先頭か末尾に持っていくソート関数', () => {
    const array = make_data(100)
    console.log(array)
    const sort_function = test_sort_hof_3([1, 11, 7, 77])(false)
    array.sort(sort_function)
    console.log(array)
  })
})

実行結果
特定の値が1つの場合

[
  38, 90, 70, 23, 79, 27, 49, 43,  0, 89, 18, 30,
  80, 50, 23, 58, 92, 12, 15,  8, 84, 97, 81, 33,
  63, 30, 27, 18, 50, 86, 78, 89, 81, 38, 56, 76,
  76, 94, 85, 21, 71, 56, 25, 10, 93, 64, 47, 67,
  85, 18,  5, 68, 74, 63, 23, 86, 31, 69, 81, 94,
  59, 11, 89, 68, 99, 65, 29, 72, 18, 96,  7, 63,
  13, 38, 50, 25, 45, 33, 96,  3, 65, 49, 91, 42,
  75, 25, 59, 94, 87, 78,  2, 13, 86, 47, 98, 81,
  98, 63, 99, 25
]
[
   7,  0,  2,  3,  5,  8, 10, 11, 12, 13, 13, 15,
  18, 18, 18, 18, 21, 23, 23, 23, 25, 25, 25, 25,
  27, 27, 29, 30, 30, 31, 33, 33, 38, 38, 38, 42,
  43, 45, 47, 47, 49, 49, 50, 50, 50, 56, 56, 58,
  59, 59, 63, 63, 63, 63, 64, 65, 65, 67, 68, 68,
  69, 70, 71, 72, 74, 75, 76, 76, 78, 78, 79, 80,
  81, 81, 81, 81, 84, 85, 85, 86, 86, 86, 87, 89,
  89, 89, 90, 91, 92, 93, 94, 94, 94, 96, 96, 97,
  98, 98, 99, 99
]

実行結果
特定の値が複数の場合

[
  58,  6, 71, 57, 69, 45, 57, 68, 49, 69, 83, 15,
  68, 51, 76, 39, 12, 79, 20, 59, 67,  9, 56, 88,
  61, 62,  6, 67, 51, 39, 11, 49, 48, 13, 45, 99,
  52, 89, 93, 43, 88, 27, 34, 59, 66, 34, 91,  7,
  47, 71, 40, 62, 82, 37, 63, 58, 90, 11, 35, 48,
  91, 79, 54, 55, 76, 45, 43, 32, 72,  3, 41, 83,
  16, 67, 50, 31, 28,  1, 96, 53,  7, 51, 37, 72,
  67, 53, 16, 26, 73, 88, 44, 39, 84, 47, 90, 83,
  77, 37,  3, 83
]
[
  99, 96, 93, 91, 91, 90, 90, 89, 88, 88, 88, 84,
  83, 83, 83, 83, 82, 79, 79, 76, 76, 73, 72, 72,
  71, 71, 69, 69, 68, 68, 67, 67, 67, 67, 66, 63,
  62, 62, 61, 59, 59, 58, 58, 57, 57, 56, 55, 54,
  53, 53, 52, 51, 51, 51, 50, 49, 49, 48, 48, 47,
  47, 45, 45, 45, 44, 43, 43, 41, 40, 39, 39, 39,
  37, 37, 37, 35, 34, 34, 32, 31, 28, 27, 26, 20,
  16, 16, 15, 13, 12,  9,  6,  6,  3,  3, 77, 11,
  11,  7,  7,  1
]

まとめ

基本関数に対して引数を増やすような拡張をする場合は
関数を返すような形で引数を追加していったほうがよさそう?

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?